diff --git a/plugin.yml b/plugin.yml index 432aeb5..d5792c3 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,13 +1,13 @@ name: LTItemMail description: A way of sending items to another players without needing to teleport to them. -version: 0.2.5 +version: 0.2.6 api-version: 1.13 -load: POSTWORLD +load: STARTUP author: [leothawne] website: https://leothawne.github.io/LTItemMail/ url: https://leothawne.github.io/LTItemMail/ main: io.github.leothawne.LTItemMail.LTItemMail -database: false +database: true soft-depend: [Vault, Essentials] commands: itemmail: diff --git a/src/main/java/io/github/leothawne/LTItemMail/App.java b/src/main/java/io/github/leothawne/LTItemMail/App.java deleted file mode 100644 index 5e29703..0000000 --- a/src/main/java/io/github/leothawne/LTItemMail/App.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.leothawne.LTItemMail; - -import java.awt.Desktop; - -import javax.swing.JOptionPane; - -public final class App { - public static final void main(final String[] args) { - final String error = "You must move this file to your /plugins/ folder!"; - System.err.println(error); - if(Desktop.isDesktopSupported()) JOptionPane.showMessageDialog(null, error, "https://leothawne.github.io/", JOptionPane.ERROR_MESSAGE); - System.exit(0); - } -} \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/LTItemMail.java b/src/main/java/io/github/leothawne/LTItemMail/LTItemMail.java index f28efea..619a1a0 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/LTItemMail.java +++ b/src/main/java/io/github/leothawne/LTItemMail/LTItemMail.java @@ -14,6 +14,7 @@ */ package io.github.leothawne.LTItemMail; +import java.sql.Connection; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; @@ -39,6 +40,7 @@ import io.github.leothawne.LTItemMail.listener.PlayerListener; import io.github.leothawne.LTItemMail.module.ConfigurationModule; import io.github.leothawne.LTItemMail.module.ConsoleModule; +import io.github.leothawne.LTItemMail.module.DatabaseModule; import io.github.leothawne.LTItemMail.module.LanguageModule; import io.github.leothawne.LTItemMail.module.MetricsModule; import io.github.leothawne.LTItemMail.module.VaultModule; @@ -52,18 +54,18 @@ * */ public final class LTItemMail extends JavaPlugin { - private final ConsoleModule console = new ConsoleModule(this); + private static LTItemMail instance; private final void registerEvents(final Listener...listeners) { - for(final Listener listener : listeners) { - Bukkit.getServer().getPluginManager().registerEvents(listener, this); - } + for(final Listener listener : listeners) Bukkit.getServer().getPluginManager().registerEvents(listener, this); } - private static FileConfiguration configuration; - private static FileConfiguration language; - private static HashMap playerBusy = new HashMap(); - private static MetricsAPI metrics; - private static BukkitScheduler scheduler; - private static int versionTask = 0; + private final ConsoleModule console = new ConsoleModule(this); + private FileConfiguration configuration; + private FileConfiguration language; + private Connection con; + private HashMap playerBusy = new HashMap(); + private MetricsAPI metrics; + private BukkitScheduler scheduler; + private Economy economyPlugin; /** * * @deprecated Not for public use. @@ -71,46 +73,43 @@ private final void registerEvents(final Listener...listeners) { */ @Override public final void onEnable() { + instance = this; this.console.Hello(); this.console.info("Loading..."); - for(final Player player : getServer().getOnlinePlayers()) { - LTItemMail.playerBusy.put(player.getUniqueId(), false); - } - ConfigurationModule.check(this, this.console); - LTItemMail.configuration = ConfigurationModule.load(this, this.console); - if(LTItemMail.configuration.getBoolean("enable-plugin") == true) { - LTItemMail.metrics = MetricsModule.init(this, this.console); + for(final Player player : getServer().getOnlinePlayers()) playerBusy.put(player.getUniqueId(), false); + ConfigurationModule.check(); + configuration = ConfigurationModule.load(); + if(configuration.getBoolean("enable-plugin")) { + metrics = MetricsModule.init(); Economy economyPlugin = null; - if(LTItemMail.configuration.getBoolean("use-vault") == true) { - this.console.info("Loading Vault..."); - if(VaultModule.isVaultInstalled(this)) { - this.console.info("Vault loaded."); - this.console.info("Looking for an Economy plugin..."); - economyPlugin = VaultModule.getEconomy(this); + if(configuration.getBoolean("use-vault")) { + console.info("Loading Vault..."); + if(VaultModule.isVaultInstalled()) { + console.info("Vault loaded."); + console.info("Looking for an Economy plugin..."); + economyPlugin = VaultModule.getEconomy(); if(economyPlugin != null) { - this.console.info("Economy plugin found."); - } else { - this.console.info("Economy plugin is missing. Skipping..."); - } - } else { - this.console.info("Vault is not installed. Skipping..."); - } + console.info("Economy plugin found."); + } else console.info("Economy plugin is missing. Skipping..."); + } else console.info("Vault is not installed. Skipping..."); } - LanguageModule.check(this, this.console, LTItemMail.configuration); - LTItemMail.language = LanguageModule.load(this, this.console, LTItemMail.configuration); - this.getCommand("itemmail").setExecutor(new ItemMailCommand(this, this.console, LTItemMail.configuration, LTItemMail.language)); - this.getCommand("itemmail").setTabCompleter(new ItemMailCommandTabCompleter()); - this.getCommand("itemmailadmin").setExecutor(new ItemMailAdminCommand(this, this.console, LTItemMail.configuration, LTItemMail.language)); - this.getCommand("itemmailadmin").setTabCompleter(new ItemMailAdminCommandTabCompleter()); - this.getCommand("mailitem").setExecutor(new MailItemCommand(this, this.console, LTItemMail.configuration, LTItemMail.language)); - this.getCommand("mailitem").setTabCompleter(new MailItemCommandTabCompleter(this)); - LTItemMail.scheduler = this.getServer().getScheduler(); - LTItemMail.versionTask = scheduler.scheduleAsyncRepeatingTask(this, new VersionTask(this, this.console), 0, 20 * 60 * 60); - registerEvents(new MailboxListener(this, LTItemMail.configuration, LTItemMail.language, LTItemMail.playerBusy, economyPlugin), new PlayerListener(LTItemMail.configuration, LTItemMail.playerBusy)); - new WarnIntegrationsAPI(this, new LinkedList(Arrays.asList("Vault", "Essentials"))); + LanguageModule.check(); + language = LanguageModule.load(); + DatabaseModule.check(); + con = DatabaseModule.load(); + getCommand("itemmail").setExecutor(new ItemMailCommand()); + getCommand("itemmail").setTabCompleter(new ItemMailCommandTabCompleter()); + getCommand("itemmailadmin").setExecutor(new ItemMailAdminCommand()); + getCommand("itemmailadmin").setTabCompleter(new ItemMailAdminCommandTabCompleter()); + getCommand("mailitem").setExecutor(new MailItemCommand()); + getCommand("mailitem").setTabCompleter(new MailItemCommandTabCompleter()); + scheduler = Bukkit.getScheduler(); + scheduler.scheduleSyncRepeatingTask(this, new VersionTask(), 0, 20 * 60 * 60); + registerEvents(new MailboxListener(), new PlayerListener()); + new WarnIntegrationsAPI(new LinkedList(Arrays.asList("Vault", "Essentials"))); } else { this.console.severe("You've choosen to disable me."); - this.getServer().getPluginManager().disablePlugin(this); + Bukkit.getPluginManager().disablePlugin(this); } } /** @@ -120,10 +119,8 @@ public final void onEnable() { */ @Override public final void onDisable() { - this.console.info("Unloading..."); - if(LTItemMail.scheduler.isCurrentlyRunning(LTItemMail.versionTask) || scheduler.isQueued(LTItemMail.versionTask)) { - LTItemMail.scheduler.cancelTask(LTItemMail.versionTask); - } + console.info("Unloading..."); + scheduler.cancelTasks(this); } /** * @@ -134,6 +131,30 @@ public final void onDisable() { */ @SuppressWarnings("deprecation") public final LTItemMailAPI getAPI() { - return new LTItemMailAPI(this, LTItemMail.configuration, LTItemMail.language, LTItemMail.playerBusy, LTItemMail.metrics); + return new LTItemMailAPI(); + } + public static final LTItemMail getInstance() { + return instance; + } + public final FileConfiguration getConfiguration() { + return configuration; + } + public final FileConfiguration getLanguage() { + return language; + } + public final HashMap getPlayerBusy(){ + return playerBusy; + } + public final ConsoleModule getConsole() { + return console; + } + public final MetricsAPI getMetrics() { + return metrics; + } + public final Economy getEconomy() { + return economyPlugin; + } + public final Connection getConnection() { + return con; } } \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/api/HTTP.java b/src/main/java/io/github/leothawne/LTItemMail/api/HTTP.java index b0e7fc3..c7c3bdb 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/api/HTTP.java +++ b/src/main/java/io/github/leothawne/LTItemMail/api/HTTP.java @@ -15,9 +15,7 @@ public static final String getData(final String url) { final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8"))); final StringBuilder builder = new StringBuilder(); String string; - while((string = reader.readLine()) != null) { - builder.append(string); - } + while((string = reader.readLine()) != null) builder.append(string); return builder.toString(); } catch(final Exception exception) { return null; diff --git a/src/main/java/io/github/leothawne/LTItemMail/api/LTItemMailAPI.java b/src/main/java/io/github/leothawne/LTItemMail/api/LTItemMailAPI.java index cb89f69..619d6cb 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/api/LTItemMailAPI.java +++ b/src/main/java/io/github/leothawne/LTItemMail/api/LTItemMailAPI.java @@ -1,9 +1,9 @@ package io.github.leothawne.LTItemMail.api; -import java.util.HashMap; import java.util.LinkedList; import java.util.UUID; +import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -18,11 +18,6 @@ * */ public final class LTItemMailAPI { - private static LTItemMail plugin; - private static FileConfiguration configuration; - private static FileConfiguration language; - private static HashMap playerBusy; - private static MetricsAPI metrics; /** * * @deprecated There is no need to manually create @@ -30,13 +25,7 @@ public final class LTItemMailAPI { * you can easily use {@link LTItemMail#getAPI()}. * */ - public LTItemMailAPI(final LTItemMail plugin, final FileConfiguration configuration, final FileConfiguration language, final HashMap playerBusy, final MetricsAPI metrics) { - LTItemMailAPI.plugin = plugin; - LTItemMailAPI.configuration = configuration; - LTItemMailAPI.language = language; - LTItemMailAPI.playerBusy = playerBusy; - LTItemMailAPI.metrics = metrics; - } + public LTItemMailAPI() {} /** * * Returns a boolean type value that can be used to determine @@ -46,7 +35,7 @@ public LTItemMailAPI(final LTItemMail plugin, final FileConfiguration configurat * */ public final boolean isUsingVault() { - return LTItemMailAPI.configuration.getBoolean("use-vault"); + return LTItemMail.getInstance().getConfiguration().getBoolean("use-vault"); } /** * @@ -61,7 +50,7 @@ public final boolean isUsingVault() { * */ public final boolean isPlayerBusy(final Player player) { - return (boolean) LTItemMailAPI.playerBusy.get(player.getUniqueId()).booleanValue(); + return (boolean) LTItemMail.getInstance().getPlayerBusy().get(player.getUniqueId()).booleanValue(); } /** * @@ -76,7 +65,7 @@ public final boolean isPlayerBusy(final Player player) { * */ public final boolean isPlayerBusy(final UUID playerUUID) { - return this.isPlayerBusy(LTItemMailAPI.plugin.getServer().getPlayer(playerUUID)); + return isPlayerBusy(Bukkit.getPlayer(playerUUID)); } /** * @@ -91,7 +80,7 @@ public final boolean isPlayerBusy(final UUID playerUUID) { * */ public final boolean isPlayerBusy(final String playerName) { - return this.isPlayerBusy(LTItemMailAPI.plugin.getServer().getPlayer(playerName)); + return isPlayerBusy(Bukkit.getPlayer(playerName)); } /** * @@ -102,18 +91,7 @@ public final boolean isPlayerBusy(final String playerName) { * */ public final FileConfiguration getLanguageMap(){ - return LTItemMailAPI.language; - } - /** - * - * Returns a boolean type value that can be used to determine - * if the plugin is currently using bStats. - * - * @return A boolean type value. - * - */ - public final boolean isMetricsEnabled() { - return LTItemMailAPI.metrics.isEnabled(); + return LTItemMail.getInstance().getLanguage(); } /** * @@ -126,7 +104,7 @@ public final boolean isMetricsEnabled() { * */ public final void sendSpecialMailbox(final Player player, final LinkedList items) { - MailboxAPI.sendSpecial(LTItemMailAPI.plugin, LTItemMailAPI.configuration, LTItemMailAPI.language, LTItemMailAPI.playerBusy, player, items); + MailboxAPI.sendSpecial(player, items); } /** * @@ -139,7 +117,7 @@ public final void sendSpecialMailbox(final Player player, final LinkedList items) { - this.sendSpecialMailbox(LTItemMailAPI.plugin.getServer().getPlayer(playerUUID), items); + sendSpecialMailbox(Bukkit.getPlayer(playerUUID), items); } /** * @@ -152,6 +130,6 @@ public final void sendSpecialMailbox(final UUID playerUUID, final LinkedList items) { - this.sendSpecialMailbox(LTItemMailAPI.plugin.getServer().getPlayer(playerName), items); + sendSpecialMailbox(Bukkit.getPlayer(playerName), items); } } \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/api/MailboxAPI.java b/src/main/java/io/github/leothawne/LTItemMail/api/MailboxAPI.java index 51043cf..6843539 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/api/MailboxAPI.java +++ b/src/main/java/io/github/leothawne/LTItemMail/api/MailboxAPI.java @@ -1,11 +1,8 @@ package io.github.leothawne.LTItemMail.api; -import java.util.HashMap; import java.util.LinkedList; -import java.util.UUID; import org.bukkit.ChatColor; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; @@ -15,21 +12,21 @@ import io.github.leothawne.LTItemMail.type.MailboxType; public final class MailboxAPI { - public static final void sendSpecial(final LTItemMail plugin, final FileConfiguration configuration, final FileConfiguration language, final HashMap playerBusy, final Player receiver, final LinkedList items) { - final String mailboxFrom = language.getString("special-mailbox"); - final String[] mailboxOpening = language.getString("mailbox-opening-seconds").split("%"); - playerBusy.put(receiver.getUniqueId(), true); - if(configuration.getBoolean("use-title") == true) { - receiver.sendTitle(ChatColor.LIGHT_PURPLE + "" + mailboxFrom, ChatColor.AQUA + "" + mailboxOpening[0] + "" + ChatColor.GREEN + "" + configuration.getInt("mail-time") + "" + ChatColor.AQUA + "" + mailboxOpening[1] + " " + ChatColor.DARK_RED + "" + language.getString("mailbox-lose"), 20 * 1, 20 * configuration.getInt("mail-time"), 20 * 1); + public static final void sendSpecial(final Player receiver, final LinkedList items) { + final String mailboxFrom = LTItemMail.getInstance().getLanguage().getString("special-mailbox"); + final String[] mailboxOpening = LTItemMail.getInstance().getLanguage().getString("mailbox-opening-seconds").split("%"); + LTItemMail.getInstance().getPlayerBusy().put(receiver.getUniqueId(), true); + if(LTItemMail.getInstance().getConfiguration().getBoolean("use-title") == true) { + receiver.sendTitle(ChatColor.LIGHT_PURPLE + "" + mailboxFrom, ChatColor.AQUA + "" + mailboxOpening[0] + "" + ChatColor.GREEN + "" + LTItemMail.getInstance().getConfiguration().getInt("mail-time") + "" + ChatColor.AQUA + "" + mailboxOpening[1] + " " + ChatColor.DARK_RED + "" + LTItemMail.getInstance().getLanguage().getString("mailbox-lose"), 20 * 1, 20 * LTItemMail.getInstance().getConfiguration().getInt("mail-time"), 20 * 1); } else { - receiver.sendMessage(ChatColor.DARK_GREEN + "[" + configuration.getString("plugin-tag") + "] " + ChatColor.AQUA + "" + mailboxFrom); - receiver.sendMessage(ChatColor.DARK_GREEN + "[" + configuration.getString("plugin-tag") + "] " + ChatColor.AQUA + "" + mailboxOpening[0] + "" + ChatColor.GREEN + "" + configuration.getInt("mail-time") + "" + ChatColor.AQUA + "" + mailboxOpening[1] + " " + ChatColor.DARK_RED + "" + language.getString("mailbox-lose")); + receiver.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.AQUA + "" + mailboxFrom); + receiver.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.AQUA + "" + mailboxOpening[0] + "" + ChatColor.GREEN + "" + LTItemMail.getInstance().getConfiguration().getInt("mail-time") + "" + ChatColor.AQUA + "" + mailboxOpening[1] + " " + ChatColor.DARK_RED + "" + LTItemMail.getInstance().getLanguage().getString("mailbox-lose")); } new BukkitRunnable() { @Override public final void run() { receiver.openInventory(MailboxInventory.getMailboxInventory(MailboxType.IN, null, items)); } - }.runTaskLater(plugin, 20 * configuration.getInt("mail-time") + 2); + }.runTaskLater(LTItemMail.getInstance(), 20 * LTItemMail.getInstance().getConfiguration().getInt("mail-time") + 2); } } \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/api/MetricsAPI.java b/src/main/java/io/github/leothawne/LTItemMail/api/MetricsAPI.java index a537df8..304036d 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/api/MetricsAPI.java +++ b/src/main/java/io/github/leothawne/LTItemMail/api/MetricsAPI.java @@ -1,3 +1,17 @@ +/* + * This Metrics class was auto-generated and can be copied into your project if you are + * not using a build tool like Gradle or Maven for dependency management. + * + * IMPORTANT: You are not allowed to modify this class, except changing the package. + * + * Disallowed modifications include but are not limited to: + * - Remove the option for users to opt-out + * - Change the frequency for data submission + * - Obfuscate the code (every obfuscator should allow you to make an exception for specific files) + * - Reformat the code (if you use a linter, add an exception) + * + * Violations will result in a ban of your plugin and account from bStats. + */ package io.github.leothawne.LTItemMail.api; import java.io.BufferedReader; @@ -6,21 +20,25 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; -import java.util.List; +import java.util.HashSet; import java.util.Map; +import java.util.Objects; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.logging.Level; +import java.util.stream.Collectors; import java.util.zip.GZIPOutputStream; import javax.net.ssl.HttpsURLConnection; @@ -29,717 +47,838 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.RegisteredServiceProvider; -import org.bukkit.plugin.ServicePriority; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; - -/** - * bStats collects some data for plugin authors. - *

- * Check out https://bStats.org/ to learn more about bStats! - */ -@SuppressWarnings({"unused"}) +import org.bukkit.plugin.java.JavaPlugin; + public class MetricsAPI { - static { - // You can use the property to disable the check in your test environment - if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { - // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D - final String defaultPackage = new String( - new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'}); - final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); - // We want to make sure nobody just copy & pastes the example and use the wrong package names - if (MetricsAPI.class.getPackage().getName().equals(defaultPackage) || MetricsAPI.class.getPackage().getName().equals(examplePackage)) { - throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); - } - } + private final Plugin plugin; + + private final MetricsBase metricsBase; + + /** + * Creates a new Metrics instance. + * + * @param plugin Your plugin instance. + * @param serviceId The id of the service. It can be found at What is my plugin id? + */ + @SuppressWarnings("deprecation") +public MetricsAPI(JavaPlugin plugin, int serviceId) { + this.plugin = plugin; + // Get the config file + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + if (!config.isSet("serverUuid")) { + config.addDefault("enabled", true); + config.addDefault("serverUuid", UUID.randomUUID().toString()); + config.addDefault("logFailedRequests", false); + config.addDefault("logSentData", false); + config.addDefault("logResponseStatusText", false); + // Inform the server owners about bStats + config + .options() + .header( + "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" + + "many people use their plugin and their total player count. It's recommended to keep bStats\n" + + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" + + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" + + "anonymous.") + .copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { + } + } + // Load the data + boolean enabled = config.getBoolean("enabled", true); + String serverUUID = config.getString("serverUuid"); + boolean logErrors = config.getBoolean("logFailedRequests", false); + boolean logSentData = config.getBoolean("logSentData", false); + boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); + metricsBase = + new MetricsBase( + "bukkit", + serverUUID, + serviceId, + enabled, + this::appendPlatformData, + this::appendServiceData, + submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), + plugin::isEnabled, + (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), + (message) -> this.plugin.getLogger().log(Level.INFO, message), + logErrors, + logSentData, + logResponseStatusText); + } + + /** Shuts down the underlying scheduler service. */ + public void shutdown() { + metricsBase.shutdown(); + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + metricsBase.addCustomChart(chart); + } + + private void appendPlatformData(JsonObjectBuilder builder) { + builder.appendField("playerAmount", getPlayerAmount()); + builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0); + builder.appendField("bukkitVersion", Bukkit.getVersion()); + builder.appendField("bukkitName", Bukkit.getName()); + builder.appendField("javaVersion", System.getProperty("java.version")); + builder.appendField("osName", System.getProperty("os.name")); + builder.appendField("osArch", System.getProperty("os.arch")); + builder.appendField("osVersion", System.getProperty("os.version")); + builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); + } + + private void appendServiceData(JsonObjectBuilder builder) { + builder.appendField("pluginVersion", plugin.getDescription().getVersion()); + } + + private int getPlayerAmount() { + try { + // Around MC 1.8 the return type was changed from an array to a collection, + // This fixes java.lang.NoSuchMethodError: + // org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; + Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); + return onlinePlayersMethod.getReturnType().equals(Collection.class) + ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() + : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; + } catch (Exception e) { + // Just use the new method if the reflection failed + return Bukkit.getOnlinePlayers().size(); } + } + + public static class MetricsBase { + + /** The version of the Metrics class. */ + public static final String METRICS_VERSION = "3.0.2"; + + private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; + + private final ScheduledExecutorService scheduler; + + private final String platform; - // This ThreadFactory enforces the naming convention for our Threads - private final ThreadFactory threadFactory = task -> new Thread(task, "bStats-Metrics"); + private final String serverUuid; - // Executor service for requests - // We use an executor service because the Bukkit scheduler is affected by server lags - private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, threadFactory); + private final int serviceId; - // The version of this bStats class - public static final int B_STATS_VERSION = 1; + private final Consumer appendPlatformDataConsumer; - // The url to which the data is sent - private static final String URL = "https://bStats.org/submitData/bukkit"; + private final Consumer appendServiceDataConsumer; - // Is bStats enabled on this server? - private boolean enabled; + private final Consumer submitTaskConsumer; - // Should failed requests be logged? - private static boolean logFailedRequests; + private final Supplier checkServiceEnabledSupplier; - // Should the sent data be logged? - private static boolean logSentData; + private final BiConsumer errorLogger; - // Should the response text be logged? - private static boolean logResponseStatusText; + private final Consumer infoLogger; - // The uuid of the server - private static String serverUUID; + private final boolean logErrors; - // The plugin - private final Plugin plugin; + private final boolean logSentData; - // The plugin id - private final int pluginId; + private final boolean logResponseStatusText; - // A list with all custom charts - private final List charts = new ArrayList<>(); + private final Set customCharts = new HashSet<>(); + + private final boolean enabled; /** - * Class constructor. + * Creates a new MetricsBase class instance. * - * @param plugin The plugin which stats should be submitted. - * @param pluginId The id of the plugin. - * It can be found at What is my plugin id? + * @param platform The platform of the service. + * @param serviceId The id of the service. + * @param serverUuid The server uuid. + * @param enabled Whether or not data sending is enabled. + * @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all platform-specific data. + * @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all service-specific data. + * @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be + * used to delegate the data collection to a another thread to prevent errors caused by + * concurrency. Can be {@code null}. + * @param checkServiceEnabledSupplier A supplier to check if the service is still enabled. + * @param errorLogger A consumer that accepts log message and an error. + * @param infoLogger A consumer that accepts info log messages. + * @param logErrors Whether or not errors should be logged. + * @param logSentData Whether or not the sent data should be logged. + * @param logResponseStatusText Whether or not the response status text should be logged. */ - @SuppressWarnings("deprecation") - public MetricsAPI(Plugin plugin, int pluginId) { - if (plugin == null) { - throw new IllegalArgumentException("Plugin cannot be null!"); - } - this.plugin = plugin; - this.pluginId = pluginId; - - // Get the config file - File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); - File configFile = new File(bStatsFolder, "config.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); - - // Check if the config file exists - if (!config.isSet("serverUuid")) { - - // Add default values - config.addDefault("enabled", true); - // Every server gets it's unique random id. - config.addDefault("serverUuid", UUID.randomUUID().toString()); - // Should failed request be logged? - config.addDefault("logFailedRequests", false); - // Should the sent data be logged? - config.addDefault("logSentData", false); - // Should the response text be logged? - config.addDefault("logResponseStatusText", false); - - // Inform the server owners about bStats - config.options().header( - "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + - "To honor their work, you should not disable it.\n" + - "This has nearly no effect on the server performance!\n" + - "Check out https://bStats.org/ to learn more :)" - ).copyDefaults(true); - try { - config.save(configFile); - } catch (IOException ignored) { } - } + public MetricsBase( + String platform, + String serverUuid, + int serviceId, + boolean enabled, + Consumer appendPlatformDataConsumer, + Consumer appendServiceDataConsumer, + Consumer submitTaskConsumer, + Supplier checkServiceEnabledSupplier, + BiConsumer errorLogger, + Consumer infoLogger, + boolean logErrors, + boolean logSentData, + boolean logResponseStatusText) { + ScheduledThreadPoolExecutor scheduler = + new ScheduledThreadPoolExecutor(1, task -> new Thread(task, "bStats-Metrics")); + // We want delayed tasks (non-periodic) that will execute in the future to be + // cancelled when the scheduler is shutdown. + // Otherwise, we risk preventing the server from shutting down even when + // MetricsBase#shutdown() is called + scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + this.scheduler = scheduler; + this.platform = platform; + this.serverUuid = serverUuid; + this.serviceId = serviceId; + this.enabled = enabled; + this.appendPlatformDataConsumer = appendPlatformDataConsumer; + this.appendServiceDataConsumer = appendServiceDataConsumer; + this.submitTaskConsumer = submitTaskConsumer; + this.checkServiceEnabledSupplier = checkServiceEnabledSupplier; + this.errorLogger = errorLogger; + this.infoLogger = infoLogger; + this.logErrors = logErrors; + this.logSentData = logSentData; + this.logResponseStatusText = logResponseStatusText; + checkRelocation(); + if (enabled) { + // WARNING: Removing the option to opt-out will get your plugin banned from + // bStats + startSubmitting(); + } + } + + public void addCustomChart(CustomChart chart) { + this.customCharts.add(chart); + } + + public void shutdown() { + scheduler.shutdown(); + } - // Load the data - enabled = config.getBoolean("enabled", true); - serverUUID = config.getString("serverUuid"); - logFailedRequests = config.getBoolean("logFailedRequests", false); - logSentData = config.getBoolean("logSentData", false); - logResponseStatusText = config.getBoolean("logResponseStatusText", false); - - if (enabled) { - boolean found = false; - // Search for all other bStats Metrics classes to see if we are the first one - for (Class service : Bukkit.getServicesManager().getKnownServices()) { - try { - service.getField("B_STATS_VERSION"); // Our identifier :) - found = true; // We aren't the first - break; - } catch (NoSuchFieldException ignored) { } + private void startSubmitting() { + final Runnable submitTask = + () -> { + if (!enabled || !checkServiceEnabledSupplier.get()) { + // Submitting data or service is disabled + scheduler.shutdown(); + return; } - // Register our service - Bukkit.getServicesManager().register(MetricsAPI.class, this, plugin, ServicePriority.Normal); - if (!found) { - // We are the first! - startSubmitting(); + if (submitTaskConsumer != null) { + submitTaskConsumer.accept(this::submitData); + } else { + this.submitData(); } - } + }; + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven + // distribution of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into + // the initial and second delay. + // WARNING: You must not modify and part of this Metrics class, including the + // submit delay or frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just + // don't do it! + long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); + long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); + scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); + scheduler.scheduleAtFixedRate( + submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + } + + private void submitData() { + final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); + appendPlatformDataConsumer.accept(baseJsonBuilder); + final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); + appendServiceDataConsumer.accept(serviceJsonBuilder); + JsonObjectBuilder.JsonObject[] chartData = + customCharts.stream() + .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) + .filter(Objects::nonNull) + .toArray(JsonObjectBuilder.JsonObject[]::new); + serviceJsonBuilder.appendField("id", serviceId); + serviceJsonBuilder.appendField("customCharts", chartData); + baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); + baseJsonBuilder.appendField("serverUUID", serverUuid); + baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); + JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); + scheduler.execute( + () -> { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logErrors) { + errorLogger.accept("Could not submit bStats metrics data", e); + } + } + }); + } + + private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { + if (logSentData) { + infoLogger.accept("Sent bStats metrics data: " + data.toString()); + } + String url = String.format(REPORT_URL, platform); + HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("User-Agent", "Metrics-Service/1"); + connection.setDoOutput(true); + try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { + outputStream.write(compressedData); + } + StringBuilder builder = new StringBuilder(); + try (BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + } + } + if (logResponseStatusText) { + infoLogger.accept("Sent data to bStats and received response: " + builder); + } + } + + /** Checks that the class was properly relocated. */ + private void checkRelocation() { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null + || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this + // little "trick" ... :D + final String defaultPackage = + new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); + final String examplePackage = + new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure no one just copy & pastes the example and uses the wrong + // package names + if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) + || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } } /** - * Checks if bStats is enabled. + * Gzips the given string. * - * @return Whether bStats is enabled or not. + * @param str The string to gzip. + * @return The gzipped string. */ - public boolean isEnabled() { - return enabled; + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + } + return outputStream.toByteArray(); } + } + + public static class SimplePie extends CustomChart { + + private final Callable callable; /** - * Adds a custom chart. + * Class constructor. * - * @param chart The chart to add. + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. */ - public void addCustomChart(CustomChart chart) { - if (chart == null) { - throw new IllegalArgumentException("Chart cannot be null!"); - } - charts.add(chart); + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); } + } + + public static class MultiLineChart extends CustomChart { + + private final Callable> callable; /** - * Starts the Scheduler which submits our data every 30 minutes. + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. */ - private void startSubmitting() { - final Runnable submitTask = () -> { - if (!plugin.isEnabled()) { // Plugin was disabled - scheduler.shutdown(); - return; - } - // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler - // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;) - Bukkit.getScheduler().runTask(plugin, this::submitData); - }; + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } - // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution of requests on the - // bStats backend. To circumvent this problem, we introduce some randomness into the initial and second delay. - // WARNING: You must not modify and part of this Metrics class, including the submit delay or frequency! - // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it! - long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); - long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); - scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); - scheduler.scheduleAtFixedRate(submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); } + } + + public static class AdvancedPie extends CustomChart { + + private final Callable> callable; /** - * Gets the plugin specific data. - * This method is called using Reflection. + * Class constructor. * - * @return The plugin specific data. + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. */ - public JsonObject getPluginData() { - JsonObject data = new JsonObject(); - - String pluginName = plugin.getDescription().getName(); - String pluginVersion = plugin.getDescription().getVersion(); - - data.addProperty("pluginName", pluginName); // Append the name of the plugin - data.addProperty("id", pluginId); // Append the id of the plugin - data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin - JsonArray customCharts = new JsonArray(); - for (CustomChart customChart : charts) { - // Add the data of the custom charts - JsonObject chart = customChart.getRequestJsonObject(); - if (chart == null) { // If the chart is null, we skip it - continue; - } - customCharts.add(chart); - } - data.add("customCharts", customCharts); + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } - return data; + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); } + } + + public static class SimpleBarChart extends CustomChart { + + private final Callable> callable; /** - * Gets the server specific data. + * Class constructor. * - * @return The server specific data. + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. */ - private JsonObject getServerData() { - // Minecraft specific data - int playerAmount; - try { - // Around MC 1.8 the return type was changed to a collection from an array, - // This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; - Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); - playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) - ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() - : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; - } catch (Exception e) { - playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed - } - int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; - String bukkitVersion = Bukkit.getVersion(); - String bukkitName = Bukkit.getName(); - - // OS/Java specific data - String javaVersion = System.getProperty("java.version"); - String osName = System.getProperty("os.name"); - String osArch = System.getProperty("os.arch"); - String osVersion = System.getProperty("os.version"); - int coreCount = Runtime.getRuntime().availableProcessors(); + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } - JsonObject data = new JsonObject(); + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } - data.addProperty("serverUUID", serverUUID); + public static class AdvancedBarChart extends CustomChart { - data.addProperty("playerAmount", playerAmount); - data.addProperty("onlineMode", onlineMode); - data.addProperty("bukkitVersion", bukkitVersion); - data.addProperty("bukkitName", bukkitName); + private final Callable> callable; - data.addProperty("javaVersion", javaVersion); - data.addProperty("osName", osName); - data.addProperty("osArch", osArch); - data.addProperty("osVersion", osVersion); - data.addProperty("coreCount", coreCount); + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } - return data; + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); } + } + + public static class DrilldownPie extends CustomChart { + + private final Callable>> callable; /** - * Collects the data and sends it afterwards. + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. */ - @SuppressWarnings("deprecation") - private void submitData() { - final JsonObject data = getServerData(); + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; + } - JsonArray pluginData = new JsonArray(); - // Search for all other bStats Metrics classes to get their plugin data - for (Class service : Bukkit.getServicesManager().getKnownServices()) { - try { - service.getField("B_STATS_VERSION"); // Our identifier :) - - for (RegisteredServiceProvider provider : Bukkit.getServicesManager().getRegistrations(service)) { - try { - Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider()); - if (plugin instanceof JsonObject) { - pluginData.add((JsonObject) plugin); - } else { // old bstats version compatibility - try { - Class jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject"); - if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) { - Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString"); - jsonStringGetter.setAccessible(true); - String jsonString = (String) jsonStringGetter.invoke(plugin); - JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject(); - pluginData.add(object); - } - } catch (ClassNotFoundException e) { - // minecraft version 1.14+ - if (logFailedRequests) { - this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception", e); - } - } - } - } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } - } - } catch (NoSuchFieldException ignored) { } - } + @Override + public JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JsonObjectBuilder valueBuilder = new JsonObjectBuilder(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build()); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } - data.add("plugins", pluginData); + public abstract static class CustomChart { - // Create a new thread for the connection to the bStats server - new Thread(() -> { - try { - // Send the data - sendData(plugin, data); - } catch (Exception e) { - // Something went wrong! :( - if (logFailedRequests) { - plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); - } - } - }).start(); + private final String chartId; + + protected CustomChart(String chartId) { + if (chartId == null) { + throw new IllegalArgumentException("chartId must not be null"); + } + this.chartId = chartId; } - /** - * Sends the data to the bStats server. - * - * @param plugin Any plugin. It's just used to get a logger instance. - * @param data The data to send. - * @throws Exception If the request failed. - */ - private static void sendData(Plugin plugin, JsonObject data) throws Exception { + public JsonObjectBuilder.JsonObject getRequestJsonObject( + BiConsumer errorLogger, boolean logErrors) { + JsonObjectBuilder builder = new JsonObjectBuilder(); + builder.appendField("chartId", chartId); + try { + JsonObjectBuilder.JsonObject data = getChartData(); if (data == null) { - throw new IllegalArgumentException("Data cannot be null!"); - } - if (Bukkit.isPrimaryThread()) { - throw new IllegalAccessException("This method must not be called from the main thread!"); + // If the data is null we don't send the chart. + return null; } - if (logSentData) { - plugin.getLogger().info("Sending data to bStats: " + data); - } - HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); - - // Compress the data to save bandwidth - byte[] compressedData = compress(data.toString()); - - // Add headers - connection.setRequestMethod("POST"); - connection.addRequestProperty("Accept", "application/json"); - connection.addRequestProperty("Connection", "close"); - connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request - connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); - connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format - connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); - - // Send data - connection.setDoOutput(true); - try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { - outputStream.write(compressedData); + builder.appendField("data", data); + } catch (Throwable t) { + if (logErrors) { + errorLogger.accept("Failed to get data for custom chart with id " + chartId, t); } + return null; + } + return builder.build(); + } - StringBuilder builder = new StringBuilder(); - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { - String line; - while ((line = bufferedReader.readLine()) != null) { - builder.append(line); - } - } + protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; + } - if (logResponseStatusText) { - plugin.getLogger().info("Sent data to bStats and received response: " + builder); - } - } + public static class SingleLineChart extends CustomChart { + + private final Callable callable; /** - * Gzips the given String. + * Class constructor. * - * @param str The string to gzip. - * @return The gzipped String. - * @throws IOException If the compression failed. + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. */ - private static byte[] compress(final String str) throws IOException { - if (str == null) { - return null; - } - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { - gzip.write(str.getBytes(StandardCharsets.UTF_8)); - } - return outputStream.toByteArray(); + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; } - /** - * Represents a custom chart. - */ - public static abstract class CustomChart { - - // The id of the chart - final String chartId; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - */ - CustomChart(String chartId) { - if (chartId == null || chartId.isEmpty()) { - throw new IllegalArgumentException("ChartId cannot be null or empty!"); - } - this.chartId = chartId; - } + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } - private JsonObject getRequestJsonObject() { - JsonObject chart = new JsonObject(); - chart.addProperty("chartId", chartId); - try { - JsonObject data = getChartData(); - if (data == null) { - // If the data is null we don't send the chart. - return null; - } - chart.add("data", data); - } catch (Throwable t) { - if (logFailedRequests) { - Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); - } - return null; - } - return chart; - } + /** + * An extremely simple JSON builder. + * + *

While this class is neither feature-rich nor the most performant one, it's sufficient enough + * for its use-case. + */ + public static class JsonObjectBuilder { + + private StringBuilder builder = new StringBuilder(); - protected abstract JsonObject getChartData() throws Exception; + private boolean hasAtLeastOneField = false; + public JsonObjectBuilder() { + builder.append("{"); } /** - * Represents a custom simple pie. + * Appends a null field to the JSON. + * + * @param key The key of the field. + * @return A reference to this object. */ - public static class SimplePie extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimplePie(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - String value = callable.call(); - if (value == null || value.isEmpty()) { - // Null = skip the chart - return null; - } - data.addProperty("value", value); - return data; - } + public JsonObjectBuilder appendNull(String key) { + appendFieldUnescaped(key, "null"); + return this; } /** - * Represents a custom advanced pie. + * Appends a string field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. */ - public static class AdvancedPie extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public AdvancedPie(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - JsonObject values = new JsonObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - continue; // Skip this invalid - } - allSkipped = false; - values.addProperty(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - data.add("values", values); - return data; - } + public JsonObjectBuilder appendField(String key, String value) { + if (value == null) { + throw new IllegalArgumentException("JSON value must not be null"); + } + appendFieldUnescaped(key, "\"" + escape(value) + "\""); + return this; } /** - * Represents a custom drilldown pie. + * Appends an integer field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. */ - public static class DrilldownPie extends CustomChart { - - private final Callable>> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public DrilldownPie(String chartId, Callable>> callable) { - super(chartId); - this.callable = callable; - } - - @Override - public JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - JsonObject values = new JsonObject(); - Map> map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean reallyAllSkipped = true; - for (Map.Entry> entryValues : map.entrySet()) { - JsonObject value = new JsonObject(); - boolean allSkipped = true; - for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { - value.addProperty(valueEntry.getKey(), valueEntry.getValue()); - allSkipped = false; - } - if (!allSkipped) { - reallyAllSkipped = false; - values.add(entryValues.getKey(), value); - } - } - if (reallyAllSkipped) { - // Null = skip the chart - return null; - } - data.add("values", values); - return data; - } + public JsonObjectBuilder appendField(String key, int value) { + appendFieldUnescaped(key, String.valueOf(value)); + return this; } /** - * Represents a custom single line chart. + * Appends an object to the JSON. + * + * @param key The key of the field. + * @param object The object. + * @return A reference to this object. */ - public static class SingleLineChart extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SingleLineChart(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - int value = callable.call(); - if (value == 0) { - // Null = skip the chart - return null; - } - data.addProperty("value", value); - return data; - } - + public JsonObjectBuilder appendField(String key, JsonObject object) { + if (object == null) { + throw new IllegalArgumentException("JSON object must not be null"); + } + appendFieldUnescaped(key, object.toString()); + return this; } /** - * Represents a custom multi line chart. + * Appends a string array to the JSON. + * + * @param key The key of the field. + * @param values The string array. + * @return A reference to this object. */ - public static class MultiLineChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public MultiLineChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } + public JsonObjectBuilder appendField(String key, String[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values) + .map(value -> "\"" + escape(value) + "\"") + .collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - JsonObject values = new JsonObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - continue; // Skip this invalid - } - allSkipped = false; - values.addProperty(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - data.add("values", values); - return data; - } + /** + * Appends an integer array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + /** + * Appends an object array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; } /** - * Represents a custom simple bar chart. + * Appends a field to the object. + * + * @param key The key of the field. + * @param escapedValue The escaped value of the field. */ - public static class SimpleBarChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimpleBarChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } + private void appendFieldUnescaped(String key, String escapedValue) { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + if (key == null) { + throw new IllegalArgumentException("JSON key must not be null"); + } + if (hasAtLeastOneField) { + builder.append(","); + } + builder.append("\"").append(escape(key)).append("\":").append(escapedValue); + hasAtLeastOneField = true; + } - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - JsonObject values = new JsonObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - for (Map.Entry entry : map.entrySet()) { - JsonArray categoryValues = new JsonArray(); - categoryValues.add(new JsonPrimitive(entry.getValue())); - values.add(entry.getKey(), categoryValues); - } - data.add("values", values); - return data; - } + /** + * Builds the JSON string and invalidates this builder. + * + * @return The built JSON string. + */ + public JsonObject build() { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + JsonObject object = new JsonObject(builder.append("}").toString()); + builder = null; + return object; + } + /** + * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt. + * + *

This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'. + * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n"). + * + * @param value The value to escape. + * @return The escaped value. + */ + private static String escape(String value) { + final StringBuilder builder = new StringBuilder(); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '"') { + builder.append("\\\""); + } else if (c == '\\') { + builder.append("\\\\"); + } else if (c <= '\u000F') { + builder.append("\\u000").append(Integer.toHexString(c)); + } else if (c <= '\u001F') { + builder.append("\\u00").append(Integer.toHexString(c)); + } else { + builder.append(c); + } + } + return builder.toString(); } /** - * Represents a custom advanced bar chart. + * A super simple representation of a JSON object. + * + *

This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not + * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String, + * JsonObject)}. */ - public static class AdvancedBarChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public AdvancedBarChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } + public static class JsonObject { - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - JsonObject values = new JsonObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue().length == 0) { - continue; // Skip this invalid - } - allSkipped = false; - JsonArray categoryValues = new JsonArray(); - for (int categoryValue : entry.getValue()) { - categoryValues.add(new JsonPrimitive(categoryValue)); - } - values.add(entry.getKey(), categoryValues); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - data.add("values", values); - return data; - } + private final String value; + + private JsonObject(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } } -} \ No newline at end of file + } +} diff --git a/src/main/java/io/github/leothawne/LTItemMail/api/WarnIntegrationsAPI.java b/src/main/java/io/github/leothawne/LTItemMail/api/WarnIntegrationsAPI.java index 1581163..796989e 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/api/WarnIntegrationsAPI.java +++ b/src/main/java/io/github/leothawne/LTItemMail/api/WarnIntegrationsAPI.java @@ -2,19 +2,18 @@ import java.util.LinkedList; +import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import io.github.leothawne.LTItemMail.LTItemMail; public final class WarnIntegrationsAPI { - public WarnIntegrationsAPI(final LTItemMail mainPlugin, final LinkedList plugins) { - final PluginManager manager = mainPlugin.getServer().getPluginManager(); + public WarnIntegrationsAPI(final LinkedList plugins) { + final PluginManager manager = Bukkit.getPluginManager(); for(final String plugin : plugins) { final Plugin getPlugin = manager.getPlugin(plugin); - if(getPlugin != null && getPlugin.isEnabled()) { - getPlugin.getLogger().info(mainPlugin.getName() + " were successfully hooked into " + getPlugin.getName() + "!"); - } + if(getPlugin != null && getPlugin.isEnabled()) LTItemMail.getInstance().getConsole().info(LTItemMail.getInstance().getName() + " were successfully hooked into " + getPlugin.getName() + "!"); } } } \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/command/ItemMailAdminCommand.java b/src/main/java/io/github/leothawne/LTItemMail/command/ItemMailAdminCommand.java index e18272a..969eae2 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/command/ItemMailAdminCommand.java +++ b/src/main/java/io/github/leothawne/LTItemMail/command/ItemMailAdminCommand.java @@ -4,74 +4,52 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.scheduler.BukkitRunnable; import io.github.leothawne.LTItemMail.LTItemMail; import io.github.leothawne.LTItemMail.api.HTTP; -import io.github.leothawne.LTItemMail.module.ConsoleModule; import io.github.leothawne.LTItemMail.module.DataModule; public final class ItemMailAdminCommand implements CommandExecutor { - private static LTItemMail plugin; - private static ConsoleModule console; - private static FileConfiguration configuration; - private static FileConfiguration language; - public ItemMailAdminCommand(final LTItemMail plugin, final ConsoleModule console, final FileConfiguration configuration, final FileConfiguration language) { - ItemMailAdminCommand.plugin = plugin; - ItemMailAdminCommand.console = console; - ItemMailAdminCommand.configuration = configuration; - ItemMailAdminCommand.language = language; - } + public ItemMailAdminCommand() {} @Override - public final boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { - if(sender.hasPermission("LTItemMail.use")) { - if(sender.hasPermission("LTItemMail.admin")) { - if(args.length == 0) { - sender.sendMessage(ChatColor.AQUA + "=+=+=+= [LT Item Mail :: Admin] =+=+=+="); - sender.sendMessage(ChatColor.GREEN + "/itemmailadmin " + ChatColor.AQUA + "- Shows commands for administrators."); - sender.sendMessage(ChatColor.GREEN + "/itemmailadmin update " + ChatColor.AQUA + "- Checks for new updates."); - sender.sendMessage(ChatColor.YELLOW + "You can also use "+ ChatColor.GREEN + "/itemmailadmin "+ ChatColor.YELLOW + "as "+ ChatColor.GREEN + "/imad"+ ChatColor.YELLOW + "."); - } else if(args[0].equalsIgnoreCase("update")) { - if(args.length < 2) { - final CommandSender finalSender = sender; - new BukkitRunnable() { - @Override - public final void run() { - String[] LocalVersion = ItemMailAdminCommand.plugin.getDescription().getVersion().split("\\."); - int Local_VersionNumber1 = Integer.parseInt(LocalVersion[0]); - int Local_VersionNumber2 = Integer.parseInt(LocalVersion[1]); - int Local_VersionNumber3 = Integer.parseInt(LocalVersion[2]); - String[] Server1 = HTTP.getData(DataModule.getUpdateURL()).split("-"); - String[] Server2 = Server1[0].split("\\."); - int Server2_VersionNumber1 = Integer.parseInt(Server2[0]); - int Server2_VersionNumber2 = Integer.parseInt(Server2[1]); - int Server2_VersionNumber3 = Integer.parseInt(Server2[2]); - String updateMessage = ChatColor.AQUA + "[" + ItemMailAdminCommand.configuration.getString("plugin-tag") + " :: Admin] " + ChatColor.YELLOW + "A newer version is available: " + ChatColor.GREEN + Server1[0] + ChatColor.YELLOW + " (released on " + ChatColor.GREEN + Server1[1] + ChatColor.YELLOW + ")."; - if(Server2_VersionNumber1 > Local_VersionNumber1) { - finalSender.sendMessage(updateMessage); - } else if(Server2_VersionNumber1 == Local_VersionNumber1 && Server2_VersionNumber2 > Local_VersionNumber2) { - finalSender.sendMessage(updateMessage); - } else if(Server2_VersionNumber1 == Local_VersionNumber1 && Server2_VersionNumber2 == Local_VersionNumber2 && Server2_VersionNumber3 > Local_VersionNumber3) { - finalSender.sendMessage(updateMessage); - } else { - finalSender.sendMessage(ChatColor.AQUA + "[" + ItemMailAdminCommand.configuration.getString("plugin-tag") + " :: Admin] " + ChatColor.YELLOW + "The plugin is up to date!"); - } - } - }.runTaskAsynchronously(plugin); - } else { - sender.sendMessage(ChatColor.AQUA + "[" + ItemMailAdminCommand.configuration.getString("plugin-tag") + " :: Admin] " + ChatColor.YELLOW + "Too many arguments!"); - } - } else { - sender.sendMessage(ChatColor.AQUA + "[" + ItemMailAdminCommand.configuration.getString("plugin-tag") + " :: Admin] " + ChatColor.YELLOW + "Invalid command! Type " + ChatColor.GREEN + "/itemmailadmin " + ChatColor.YELLOW + "to see all available commands."); - } - } else { - sender.sendMessage(ChatColor.AQUA + "[" + ItemMailAdminCommand.configuration.getString("plugin-tag") + " :: Admin] " + ChatColor.YELLOW + "" + ItemMailAdminCommand.language.getString("no-permission")); - ItemMailAdminCommand.console.severe(sender.getName() + " does not have permission [LTItemMail.admin]."); - } + public final boolean onCommand(final CommandSender sender, final Command cmd, final String commandLabel, final String[] args) { + if(sender.hasPermission("LTItemMail.admin")) { + if(args.length == 0) { + sender.sendMessage(ChatColor.AQUA + "=+=+=+= [LT Item Mail :: Admin] =+=+=+="); + sender.sendMessage(ChatColor.GREEN + "/itemmailadmin " + ChatColor.AQUA + "- Commands for administrators."); + sender.sendMessage(ChatColor.GREEN + "/itemmailadmin update " + ChatColor.AQUA + "- Check for new updates."); + sender.sendMessage(ChatColor.YELLOW + "You can also use "+ ChatColor.GREEN + "/itemmailadmin "+ ChatColor.YELLOW + "as "+ ChatColor.GREEN + "/imad"+ ChatColor.YELLOW + "."); + } else if(args[0].equalsIgnoreCase("update")) { + if(args.length == 1) { + final CommandSender finalSender = sender; + new BukkitRunnable() { + @Override + public final void run() { + String[] LocalVersion = LTItemMail.getInstance().getDescription().getVersion().split("\\."); + int Local_VersionNumber1 = Integer.parseInt(LocalVersion[0]); + int Local_VersionNumber2 = Integer.parseInt(LocalVersion[1]); + int Local_VersionNumber3 = Integer.parseInt(LocalVersion[2]); + String[] Server1 = HTTP.getData(DataModule.getUpdateURL()).split("-"); + String[] Server2 = Server1[0].split("\\."); + int Server2_VersionNumber1 = Integer.parseInt(Server2[0]); + int Server2_VersionNumber2 = Integer.parseInt(Server2[1]); + int Server2_VersionNumber3 = Integer.parseInt(Server2[2]); + String updateMessage = ChatColor.AQUA + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + " :: Admin] " + ChatColor.YELLOW + "A newer version is available: " + ChatColor.GREEN + Server1[0] + ChatColor.YELLOW + " (released on " + ChatColor.GREEN + Server1[1] + ChatColor.YELLOW + ")."; + if(Server2_VersionNumber1 > Local_VersionNumber1) { + finalSender.sendMessage(updateMessage); + } else if(Server2_VersionNumber1 == Local_VersionNumber1 && Server2_VersionNumber2 > Local_VersionNumber2) { + finalSender.sendMessage(updateMessage); + } else if(Server2_VersionNumber1 == Local_VersionNumber1 && Server2_VersionNumber2 == Local_VersionNumber2 && Server2_VersionNumber3 > Local_VersionNumber3) { + finalSender.sendMessage(updateMessage); + } else finalSender.sendMessage(ChatColor.AQUA + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + " :: Admin] " + ChatColor.YELLOW + "The plugin is up to date!"); + } + }.runTaskAsynchronously(LTItemMail.getInstance()); + } else sender.sendMessage(ChatColor.AQUA + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + " :: Admin] " + ChatColor.YELLOW + "Too many arguments!"); + } else sender.sendMessage(ChatColor.AQUA + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + " :: Admin] " + ChatColor.YELLOW + "Invalid command! Type " + ChatColor.GREEN + "/itemmailadmin " + ChatColor.YELLOW + "to see all available commands."); } else { - sender.sendMessage(ChatColor.AQUA + "[" + ItemMailAdminCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + ItemMailAdminCommand.language.getString("no-permission")); - ItemMailAdminCommand.console.severe(sender.getName() + " does not have permission [LTItemMail.use]."); + sender.sendMessage(ChatColor.AQUA + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + " :: Admin] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("no-permission")); + LTItemMail.getInstance().getConsole().severe(sender.getName() + " does not have permission [LTItemMail.admin]."); } return true; } diff --git a/src/main/java/io/github/leothawne/LTItemMail/command/ItemMailCommand.java b/src/main/java/io/github/leothawne/LTItemMail/command/ItemMailCommand.java index d4b6b74..37d20ad 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/command/ItemMailCommand.java +++ b/src/main/java/io/github/leothawne/LTItemMail/command/ItemMailCommand.java @@ -4,46 +4,37 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.FileConfiguration; import io.github.leothawne.LTItemMail.LTItemMail; -import io.github.leothawne.LTItemMail.module.ConsoleModule; import io.github.leothawne.LTItemMail.module.DataModule; public final class ItemMailCommand implements CommandExecutor { - private static LTItemMail plugin; - private static ConsoleModule console; - private static FileConfiguration configuration; - private static FileConfiguration language; - public ItemMailCommand(final LTItemMail plugin, final ConsoleModule console, final FileConfiguration configuration, final FileConfiguration language) { - ItemMailCommand.plugin = plugin; - ItemMailCommand.console = console; - ItemMailCommand.configuration = configuration; - ItemMailCommand.language = language; - } + public ItemMailCommand() {} @Override - public final boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + public final boolean onCommand(final CommandSender sender, final Command cmd, final String commandLabel, final String[] args) { if(sender.hasPermission("LTItemMail.use")) { if(args.length == 0) { sender.sendMessage(ChatColor.AQUA + "=+=+=+= [LT Item Mail] =+=+=+="); - sender.sendMessage(ChatColor.GREEN + "/itemmail " + ChatColor.AQUA + "- Shows commands for everyone."); - sender.sendMessage(ChatColor.GREEN + "/itemmail version " + ChatColor.AQUA + "- Shows the current version."); - sender.sendMessage(ChatColor.GREEN + "/mailitem " + ChatColor.AQUA + "- Open a mailbox to put items inside."); - sender.sendMessage(ChatColor.GREEN + "/itemmailadmin " + ChatColor.AQUA + "- Shows commands for administrators."); + sender.sendMessage(ChatColor.GREEN + "/itemmail " + ChatColor.AQUA + "- Commands of the plugin."); + sender.sendMessage(ChatColor.GREEN + "/itemmail version " + ChatColor.AQUA + "- Show the current version."); + sender.sendMessage(ChatColor.GREEN + "/itemmail list " + ChatColor.AQUA + "- List pending mailboxes."); + sender.sendMessage(ChatColor.GREEN + "/itemmail open " + ChatColor.AQUA + "- Open a pending mailbox."); + sender.sendMessage(ChatColor.GREEN + "/mailitem " + ChatColor.AQUA + "- Open a new mailbox to put items inside and send to another player."); + //sender.sendMessage(ChatColor.GREEN + "/itemmailadmin " + ChatColor.AQUA + "- Commands for administrators."); sender.sendMessage(ChatColor.YELLOW + "You can also use "+ ChatColor.GREEN + "/itemmail "+ ChatColor.YELLOW + "as "+ ChatColor.GREEN + "/ima"+ ChatColor.YELLOW + "."); sender.sendMessage(ChatColor.YELLOW + "You can also use "+ ChatColor.GREEN + "/mailitem "+ ChatColor.YELLOW + "as "+ ChatColor.GREEN + "/mit"+ ChatColor.YELLOW + " or "+ ChatColor.GREEN + "/enviaritem"+ ChatColor.YELLOW + " or "+ ChatColor.GREEN + "/vit"+ ChatColor.YELLOW + "."); } else if(args[0].equalsIgnoreCase("version")) { - if(args.length < 2) { - DataModule.version(ItemMailCommand.plugin.getDescription().getVersion(), sender); - } else { - sender.sendMessage(ChatColor.AQUA + "[" + ItemMailCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "Too many arguments!"); - } - } else { - sender.sendMessage(ChatColor.AQUA + "[" + ItemMailCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "Invalid command! Type " + ChatColor.GREEN + "/itemmail " + ChatColor.YELLOW + "to see all available commands."); - } + if(args.length == 1) { + DataModule.version(LTItemMail.getInstance().getDescription().getVersion(), sender); + } else sender.sendMessage(ChatColor.AQUA + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "Too many arguments!"); + } else if(args[0].equalsIgnoreCase("open")) { + + } else if(args[0].equalsIgnoreCase("list")) { + + } else sender.sendMessage(ChatColor.AQUA + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "Invalid command! Type " + ChatColor.GREEN + "/itemmail " + ChatColor.YELLOW + "to see all available commands."); } else { - sender.sendMessage(ChatColor.AQUA + "[" + ItemMailCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + ItemMailCommand.language.getString("no-permission")); - ItemMailCommand.console.severe(sender.getName() + " does not have permission [LTItemMail.use]."); + sender.sendMessage(ChatColor.AQUA + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("no-permission")); + LTItemMail.getInstance().getConsole().severe(sender.getName() + " does not have permission [LTItemMail.use]."); } return true; } diff --git a/src/main/java/io/github/leothawne/LTItemMail/command/MailItemCommand.java b/src/main/java/io/github/leothawne/LTItemMail/command/MailItemCommand.java index 2d7e3ba..d1e3bdd 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/command/MailItemCommand.java +++ b/src/main/java/io/github/leothawne/LTItemMail/command/MailItemCommand.java @@ -4,58 +4,39 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import io.github.leothawne.LTItemMail.LTItemMail; import io.github.leothawne.LTItemMail.inventory.MailboxInventory; -import io.github.leothawne.LTItemMail.module.ConsoleModule; import io.github.leothawne.LTItemMail.type.MailboxType; public final class MailItemCommand implements CommandExecutor { - private static LTItemMail plugin; - private static ConsoleModule console; - private static FileConfiguration configuration; - private static FileConfiguration language; - public MailItemCommand(final LTItemMail plugin, final ConsoleModule console, final FileConfiguration configuration, final FileConfiguration language) { - MailItemCommand.plugin = plugin; - MailItemCommand.console = console; - MailItemCommand.configuration = configuration; - MailItemCommand.language = language; - } + public MailItemCommand() {} @Override - public final boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + public final boolean onCommand(final CommandSender sender, final Command cmd, final String commandLabel, final String[] args) { if(sender.hasPermission("LTItemMail.send")) { if(sender instanceof Player) { final Player player = (Player) sender; if(args.length < 1) { - player.sendMessage(ChatColor.DARK_GREEN + "[" + MailItemCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + MailItemCommand.language.getString("recipient-empty")); + player.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("recipient-empty")); } else if(args.length < 3) { - final Player player1 = MailItemCommand.plugin.getServer().getPlayer(args[0]); + final Player player1 = LTItemMail.getInstance().getServer().getPlayer(args[0]); if(player1 != null) { if(player1.getUniqueId().equals(player.getUniqueId())) { if(args.length == 2 && args[1].equalsIgnoreCase("--bypass") && player.hasPermission("LTItemMail.admin")) { - player.sendMessage(ChatColor.DARK_GREEN + "[" + MailItemCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "Okay..."); + player.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "Okay..."); player.openInventory(MailboxInventory.getMailboxInventory(MailboxType.OUT, player1, null)); - } else { - player.sendMessage(ChatColor.DARK_GREEN + "[" + MailItemCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + MailItemCommand.language.getString("player-self")); - } + } else player.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("player-self")); } else { - player.sendMessage(ChatColor.DARK_GREEN + "[" + MailItemCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + MailItemCommand.language.getString("mailbox-opening")); + player.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("mailbox-opening")); player.openInventory(MailboxInventory.getMailboxInventory(MailboxType.OUT, player1, null)); } - } else { - player.sendMessage(ChatColor.DARK_GREEN + "[" + MailItemCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + MailItemCommand.language.getString("recipient-offline")); - } - } else { - player.sendMessage(ChatColor.DARK_GREEN + "[" + MailItemCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + MailItemCommand.language.getString("player-tma")); - } - } else { - sender.sendMessage(ChatColor.DARK_GREEN + "[" + MailItemCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + MailItemCommand.language.getString("player-error")); - } + } else player.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("recipient-offline")); + } else player.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("player-tma")); + } else sender.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("player-error")); } else { - sender.sendMessage(ChatColor.DARK_GREEN + "[" + MailItemCommand.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + MailItemCommand.language.getString("no-permission")); - MailItemCommand.console.warning(sender.getName() + " does not have permission [LTItemMail.use]!"); + sender.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("no-permission")); + LTItemMail.getInstance().getConsole().warning(sender.getName() + " does not have permission [LTItemMail.use]!"); } return true; } diff --git a/src/main/java/io/github/leothawne/LTItemMail/command/tabCompleter/MailItemCommandTabCompleter.java b/src/main/java/io/github/leothawne/LTItemMail/command/tabCompleter/MailItemCommandTabCompleter.java index 07ed2ce..6338acd 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/command/tabCompleter/MailItemCommandTabCompleter.java +++ b/src/main/java/io/github/leothawne/LTItemMail/command/tabCompleter/MailItemCommandTabCompleter.java @@ -4,26 +4,23 @@ import java.util.LinkedList; import java.util.List; +import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabCompleter; import org.bukkit.entity.Player; -import io.github.leothawne.LTItemMail.LTItemMail; import io.github.leothawne.LTItemMail.api.TabCompleterAPI; public final class MailItemCommandTabCompleter implements TabCompleter { - private static LTItemMail plugin; - public MailItemCommandTabCompleter(final LTItemMail plugin) { - MailItemCommandTabCompleter.plugin = plugin; - } + public MailItemCommandTabCompleter() {} @Override - public final List onTabComplete(CommandSender sender, Command cmd, String commandLabel, String[] args){ + public final List onTabComplete(final CommandSender sender, final Command cmd, final String commandLabel, final String[] args){ final List ReturnNothing = new ArrayList<>(); if(sender.hasPermission("LTItemMail.send")) { if(args.length == 1) { final LinkedList players = new LinkedList(); - for(final Player player : MailItemCommandTabCompleter.plugin.getServer().getOnlinePlayers()) { + for(final Player player : Bukkit.getOnlinePlayers()) { players.add(player.getName()); } return TabCompleterAPI.partial(args[0], players); diff --git a/src/main/java/io/github/leothawne/LTItemMail/inventory/MailboxInventory.java b/src/main/java/io/github/leothawne/LTItemMail/inventory/MailboxInventory.java index 00f80ac..7432f15 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/inventory/MailboxInventory.java +++ b/src/main/java/io/github/leothawne/LTItemMail/inventory/MailboxInventory.java @@ -18,12 +18,9 @@ public static final String getMailboxName(final MailboxType type) { public static final Inventory getMailboxInventory(final MailboxType type, Player player, final LinkedList contents) { if(type.equals(MailboxType.IN)) { final Inventory inventory = Bukkit.createInventory(null, 54, getMailboxName(type)); - for(int i = 0; i < (contents.size() - 1); i++) { - inventory.setItem(i, contents.get(i)); - } + for(int i = 0; i < (contents.size() - 1); i++) inventory.setItem(i, contents.get(i)); return inventory; - } - if(type.equals(MailboxType.OUT)) { + } else if(type.equals(MailboxType.OUT)) { final Inventory inventory = Bukkit.createInventory(player, 54, getMailboxName(type)); return inventory; } diff --git a/src/main/java/io/github/leothawne/LTItemMail/listener/MailboxListener.java b/src/main/java/io/github/leothawne/LTItemMail/listener/MailboxListener.java index d314c7d..857bccc 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/listener/MailboxListener.java +++ b/src/main/java/io/github/leothawne/LTItemMail/listener/MailboxListener.java @@ -1,13 +1,10 @@ package io.github.leothawne.LTItemMail.listener; -import java.util.HashMap; import java.util.LinkedList; -import java.util.UUID; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -21,136 +18,97 @@ import io.github.leothawne.LTItemMail.LTItemMail; import io.github.leothawne.LTItemMail.inventory.MailboxInventory; import io.github.leothawne.LTItemMail.type.MailboxType; -import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.economy.EconomyResponse; public final class MailboxListener implements Listener { - private static LTItemMail plugin; - private static FileConfiguration configuration; - private static FileConfiguration language; - private static HashMap playerBusy; - private static Economy economyPlugin; - public MailboxListener(final LTItemMail plugin, final FileConfiguration configuration, final FileConfiguration language, final HashMap playerBusy, final Economy economyPlugin){ - MailboxListener.plugin = plugin; - MailboxListener.configuration = configuration; - MailboxListener.language = language; - MailboxListener.playerBusy = playerBusy; - MailboxListener.economyPlugin = economyPlugin; - } + public MailboxListener(){} @EventHandler(priority = EventPriority.MONITOR) public final void onInventoryClose(final InventoryCloseEvent event) { final Player player = (Player) event.getPlayer(); final Inventory inventory = event.getInventory(); final InventoryView inventoryView = player.getOpenInventory(); if(inventoryView.getTitle().equals(MailboxInventory.getMailboxName(MailboxType.IN))) { - MailboxListener.playerBusy.put(player.getUniqueId(), false); + LTItemMail.getInstance().getPlayerBusy().put(player.getUniqueId(), false); final ItemStack[] contents = inventory.getContents(); boolean isEmpty = true; - for(final ItemStack content : contents) { - if(content != null) { - isEmpty = false; - } - } + for(final ItemStack content : contents) if(content != null) isEmpty = false; if(isEmpty == false) { String itemslost = ""; int count = 0; - for(final ItemStack content: contents) { - if(content != null) { - if(itemslost.isBlank() && itemslost.trim().isBlank()) { - itemslost = content.getAmount() + "x " + content.getType().name(); - } else itemslost = itemslost + ", " + content.getAmount() + "x " + content.getType().name(); - count = count + content.getAmount(); - } + for(final ItemStack content: contents) if(content != null) { + if(itemslost.isBlank() && itemslost.trim().isBlank()) { + itemslost = content.getAmount() + "x " + content.getType().name(); + } else itemslost = itemslost + ", " + content.getAmount() + "x " + content.getType().name(); + count = count + content.getAmount(); } - final String[] mailboxClosedItems = MailboxListener.language.getString("mailbox-closed-items").split("%"); - player.sendMessage(ChatColor.DARK_GREEN + "[" + MailboxListener.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + language.getString("mailbox-closed") + " " + mailboxClosedItems[0] + "" + ChatColor.GREEN + "" + count + "" + ChatColor.YELLOW + "" + mailboxClosedItems[1] + " " + ChatColor.GREEN + "" + itemslost); - } else { - player.sendMessage(ChatColor.DARK_GREEN + "[" + MailboxListener.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + language.getString("mailbox-closed")); - } + final String[] mailboxClosedItems = LTItemMail.getInstance().getLanguage().getString("mailbox-closed-items").split("%"); + player.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("mailbox-closed") + " " + mailboxClosedItems[0] + "" + ChatColor.GREEN + "" + count + "" + ChatColor.YELLOW + "" + mailboxClosedItems[1] + " " + ChatColor.GREEN + "" + itemslost); + } else player.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("mailbox-closed")); inventory.clear(); - } - if(inventoryView.getTitle().equals(MailboxInventory.getMailboxName(MailboxType.OUT))) { + } else if(inventoryView.getTitle().equals(MailboxInventory.getMailboxName(MailboxType.OUT))) { final Player sender = (Player) event.getPlayer(); final Player recipient = (Player) inventory.getHolder(); final ItemStack[] contents = inventory.getContents(); final LinkedList contentsarray = new LinkedList(); - for(final ItemStack content : contents) { - if(content == null) { - contentsarray.add(new ItemStack(Material.AIR)); - } else { - contentsarray.add(content); - } - } + for(final ItemStack content : contents) if(content == null) { + contentsarray.add(new ItemStack(Material.AIR)); + } else contentsarray.add(content); boolean isEmpty = true; int count = 0; - for(final ItemStack content : contentsarray) { - if(content.getType() != Material.AIR) { - isEmpty = false; - count = count + content.getAmount(); - } + for(final ItemStack content : contentsarray) if(content.getType() != Material.AIR) { + isEmpty = false; + count = count + content.getAmount(); } inventory.clear(); - if(isEmpty == false) { - if(MailboxListener.playerBusy.get(recipient.getUniqueId()).booleanValue() == false) { + if(!isEmpty) { + if(LTItemMail.getInstance().getPlayerBusy().get(recipient.getUniqueId()).booleanValue()) { double newcost = 0; - if(configuration.getBoolean("cost-per-item") == true) { - newcost = MailboxListener.configuration.getDouble("mail-cost") * count; - } else { - newcost = MailboxListener.configuration.getDouble("mail-cost"); - } - if(MailboxListener.economyPlugin != null) { - if(MailboxListener.economyPlugin.has(sender, newcost)) { - final EconomyResponse er = MailboxListener.economyPlugin.withdrawPlayer(sender, newcost); + if(LTItemMail.getInstance().getConfiguration().getBoolean("cost-per-item")) { + newcost = LTItemMail.getInstance().getConfiguration().getDouble("mail-cost") * count; + } else newcost = LTItemMail.getInstance().getConfiguration().getDouble("mail-cost"); + if(LTItemMail.getInstance().getEconomy() != null) { + if(LTItemMail.getInstance().getEconomy().has(sender, newcost)) { + final EconomyResponse er = LTItemMail.getInstance().getEconomy().withdrawPlayer(sender, newcost); if(er.transactionSuccess()) { - final String[] mailboxPaid = MailboxListener.language.getString("mailbox-paid").split("%"); - sender.sendMessage(ChatColor.DARK_GREEN + "[" + MailboxListener.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + mailboxPaid[0] + "" + ChatColor.GREEN + newcost + "" + ChatColor.YELLOW + "" + mailboxPaid[1]); + final String[] mailboxPaid = LTItemMail.getInstance().getLanguage().getString("mailbox-paid").split("%"); + sender.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + mailboxPaid[0] + "" + ChatColor.GREEN + newcost + "" + ChatColor.YELLOW + "" + mailboxPaid[1]); sendBox(sender, recipient, contentsarray); } else { - sender.sendMessage(ChatColor.DARK_GREEN + "[" + MailboxListener.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + MailboxListener.language.getString("transaction-error")); - for(final ItemStack item : contentsarray) { - sender.getInventory().addItem(item); - } + sender.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("transaction-error")); + for(final ItemStack item : contentsarray) sender.getInventory().addItem(item); } } else { - final String[] transactionNoMoney = MailboxListener.language.getString("transaction-no-money").split("%"); - sender.sendMessage(ChatColor.DARK_GREEN + "[" + MailboxListener.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + transactionNoMoney[0] + "" + ChatColor.GREEN + newcost + "" + ChatColor.YELLOW + "" + transactionNoMoney[1]); - for(final ItemStack item : contentsarray) { - sender.getInventory().addItem(item); - } + final String[] transactionNoMoney = LTItemMail.getInstance().getLanguage().getString("transaction-no-money").split("%"); + sender.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + transactionNoMoney[0] + "" + ChatColor.GREEN + newcost + "" + ChatColor.YELLOW + "" + transactionNoMoney[1]); + for(final ItemStack item : contentsarray) sender.getInventory().addItem(item); } - } else { - sendBox(sender, recipient, contentsarray); - } + } else sendBox(sender, recipient, contentsarray); } else { - final String[] recipientBusy = MailboxListener.language.getString("recipient-busy").split("%"); - sender.sendMessage(ChatColor.DARK_GREEN + "[" + MailboxListener.configuration.getString("plugin-tag") + "] " + ChatColor.AQUA + "" + recipient.getName() + "" + ChatColor.YELLOW + "" + recipientBusy[1]); - for(final ItemStack item : contentsarray) { - sender.getInventory().addItem(item); - } + final String[] recipientBusy = LTItemMail.getInstance().getLanguage().getString("recipient-busy").split("%"); + sender.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.AQUA + "" + recipient.getName() + "" + ChatColor.YELLOW + "" + recipientBusy[1]); + for(final ItemStack item : contentsarray) sender.getInventory().addItem(item); } - } else { - sender.sendMessage(ChatColor.DARK_GREEN + "[" + MailboxListener.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + MailboxListener.language.getString("mailbox-aborted")); - } + } else sender.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("mailbox-aborted")); } } private static final void sendBox(final CommandSender sender, final Player recipient, final LinkedList contentsarray) { - final String[] mailboxSent = MailboxListener.language.getString("mailbox-sent").split("%"); - final String[] mailboxFrom = MailboxListener.language.getString("mailbox-from").split("%"); - final String[] mailboxOpening = MailboxListener.language.getString("mailbox-opening-seconds").split("%"); - sender.sendMessage(ChatColor.DARK_GREEN + "[" + MailboxListener.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + mailboxSent[0] + "" + ChatColor.AQUA + "" + recipient.getName() + "" + ChatColor.YELLOW + "" + mailboxSent[1]); - MailboxListener.playerBusy.put(recipient.getUniqueId(), true); - if(MailboxListener.configuration.getBoolean("use-title") == true) { - recipient.sendTitle(ChatColor.AQUA + "" + mailboxFrom[0] + "" + ChatColor.GREEN + "" + sender.getName(), ChatColor.AQUA + "" + mailboxOpening[0] + "" + ChatColor.GREEN + "" + MailboxListener.configuration.getInt("mail-time") + "" + ChatColor.AQUA + "" + mailboxOpening[1] + " " + ChatColor.DARK_RED + "" + MailboxListener.language.getString("mailbox-lose"), 20 * 1, 20 * MailboxListener.configuration.getInt("mail-time"), 20 * 1); + final String[] mailboxSent = LTItemMail.getInstance().getLanguage().getString("mailbox-sent").split("%"); + final String[] mailboxFrom = LTItemMail.getInstance().getLanguage().getString("mailbox-from").split("%"); + final String[] mailboxOpening = LTItemMail.getInstance().getLanguage().getString("mailbox-opening-seconds").split("%"); + sender.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + mailboxSent[0] + "" + ChatColor.AQUA + "" + recipient.getName() + "" + ChatColor.YELLOW + "" + mailboxSent[1]); + LTItemMail.getInstance().getPlayerBusy().put(recipient.getUniqueId(), true); + if(LTItemMail.getInstance().getConfiguration().getBoolean("use-title") == true) { + recipient.sendTitle(ChatColor.AQUA + "" + mailboxFrom[0] + "" + ChatColor.GREEN + "" + sender.getName(), ChatColor.AQUA + "" + mailboxOpening[0] + "" + ChatColor.GREEN + "" + LTItemMail.getInstance().getConfiguration().getInt("mail-time") + "" + ChatColor.AQUA + "" + mailboxOpening[1] + " " + ChatColor.DARK_RED + "" + LTItemMail.getInstance().getLanguage().getString("mailbox-lose"), 20 * 1, 20 * LTItemMail.getInstance().getConfiguration().getInt("mail-time"), 20 * 1); } else { - recipient.sendMessage(ChatColor.DARK_GREEN + "[" + MailboxListener.configuration.getString("plugin-tag") + "] " + ChatColor.AQUA + "" + mailboxFrom[0] + "" + ChatColor.GREEN + "" + sender.getName() + "" + ChatColor.AQUA + "" + mailboxFrom[1]); - recipient.sendMessage(ChatColor.DARK_GREEN + "[" + MailboxListener.configuration.getString("plugin-tag") + "] " + ChatColor.AQUA + "" + mailboxOpening[0] + "" + ChatColor.GREEN + "" + MailboxListener.configuration.getInt("mail-time") + "" + ChatColor.AQUA + "" + mailboxOpening[1] + " " + ChatColor.DARK_RED + "" + MailboxListener.language.getString("mailbox-lose")); + recipient.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.AQUA + "" + mailboxFrom[0] + "" + ChatColor.GREEN + "" + sender.getName() + "" + ChatColor.AQUA + "" + mailboxFrom[1]); + recipient.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.AQUA + "" + mailboxOpening[0] + "" + ChatColor.GREEN + "" + LTItemMail.getInstance().getConfiguration().getInt("mail-time") + "" + ChatColor.AQUA + "" + mailboxOpening[1] + " " + ChatColor.DARK_RED + "" + LTItemMail.getInstance().getLanguage().getString("mailbox-lose")); } new BukkitRunnable() { @Override public final void run() { recipient.openInventory(MailboxInventory.getMailboxInventory(MailboxType.IN, null, contentsarray)); - sender.sendMessage(ChatColor.DARK_GREEN + "[" + MailboxListener.configuration.getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + MailboxListener.language.getString("mailbox-delivered")); + sender.sendMessage(ChatColor.DARK_GREEN + "[" + LTItemMail.getInstance().getConfiguration().getString("plugin-tag") + "] " + ChatColor.YELLOW + "" + LTItemMail.getInstance().getLanguage().getString("mailbox-delivered")); } - }.runTaskLater(MailboxListener.plugin, 20 * MailboxListener.configuration.getInt("mail-time") + 2); + }.runTaskLater(LTItemMail.getInstance(), 20 * LTItemMail.getInstance().getConfiguration().getInt("mail-time") + 2); } } \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/listener/PlayerListener.java b/src/main/java/io/github/leothawne/LTItemMail/listener/PlayerListener.java index 4a0496f..2dfb0fe 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/listener/PlayerListener.java +++ b/src/main/java/io/github/leothawne/LTItemMail/listener/PlayerListener.java @@ -1,30 +1,19 @@ package io.github.leothawne.LTItemMail.listener; -import java.util.HashMap; -import java.util.UUID; - -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; +import io.github.leothawne.LTItemMail.LTItemMail; + public final class PlayerListener implements Listener { - private static FileConfiguration configuration; - private static HashMap playerBusy; - public PlayerListener(final FileConfiguration configuration, final HashMap playerBusy) { - PlayerListener.configuration = configuration; - PlayerListener.playerBusy = playerBusy; - } + public PlayerListener() {} @EventHandler(priority = EventPriority.MONITOR) public final void onPlayerJoin(final PlayerJoinEvent event) { final Player player = (Player) event.getPlayer(); - PlayerListener.playerBusy.put(player.getUniqueId(), false); - if(player.hasPermission("LTItemMail.use") && player.hasPermission("LTItemMail.admin")) { - if(PlayerListener.configuration.getBoolean("update.check") == true) { - player.performCommand("itemmailadmin version"); - } - } + LTItemMail.getInstance().getPlayerBusy().put(player.getUniqueId(), false); + if(player.hasPermission("LTItemMail.admin")) if(LTItemMail.getInstance().getConfiguration().getBoolean("update.check")) player.performCommand("itemmailadmin version"); } } \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/module/ConfigurationModule.java b/src/main/java/io/github/leothawne/LTItemMail/module/ConfigurationModule.java index 2190aee..256b097 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/module/ConfigurationModule.java +++ b/src/main/java/io/github/leothawne/LTItemMail/module/ConfigurationModule.java @@ -8,27 +8,23 @@ import io.github.leothawne.LTItemMail.type.VersionType; public final class ConfigurationModule { - public static final void check(final LTItemMail plugin, final ConsoleModule console) { - final File configFile = new File(plugin.getDataFolder(), "config.yml"); + public static final void check() { + final File configFile = new File(LTItemMail.getInstance().getDataFolder(), "config.yml"); if(configFile.exists() == false) { - console.warning("Extracting config.yml file..."); - plugin.saveDefaultConfig(); - console.info("Done."); - } else { - console.info("Found config.yml file."); - } + LTItemMail.getInstance().getConsole().warning("Extracting config.yml file..."); + LTItemMail.getInstance().saveDefaultConfig(); + LTItemMail.getInstance().getConsole().info("Done."); + } else LTItemMail.getInstance().getConsole().info("Found config.yml file."); } - public static final FileConfiguration load(final LTItemMail plugin, final ConsoleModule console) { - final File configFile = new File(plugin.getDataFolder(), "config.yml"); + public static final FileConfiguration load() { + final File configFile = new File(LTItemMail.getInstance().getDataFolder(), "config.yml"); if(configFile.exists()) { - final FileConfiguration configuration = plugin.getConfig(); - console.info("Loaded config.yml file."); - if(configuration.getInt("config-version") != Integer.parseInt(DataModule.getVersion(VersionType.CONFIG_YML))) { - console.severe("config.yml file outdated. Delete that file and restart the server."); - } + final FileConfiguration configuration = LTItemMail.getInstance().getConfig(); + LTItemMail.getInstance().getConsole().info("Loaded config.yml file."); + if(configuration.getInt("config-version") != Integer.parseInt(DataModule.getVersion(VersionType.CONFIG_YML))) LTItemMail.getInstance().getConsole().severe("config.yml file outdated. Delete that file and restart the server."); return configuration; } - console.severe("Missing config.yml file."); + LTItemMail.getInstance().getConsole().severe("Missing config.yml file."); return null; } } \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/module/DatabaseModule.java b/src/main/java/io/github/leothawne/LTItemMail/module/DatabaseModule.java new file mode 100644 index 0000000..0a216ab --- /dev/null +++ b/src/main/java/io/github/leothawne/LTItemMail/module/DatabaseModule.java @@ -0,0 +1,35 @@ +package io.github.leothawne.LTItemMail.module; + +import java.io.File; +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import io.github.leothawne.LTItemMail.LTItemMail; + +public final class DatabaseModule { + public static final void check() { + final File databaseFile = new File(LTItemMail.getInstance().getDataFolder(), "mailboxes.db"); + if(!databaseFile.exists()) { + LTItemMail.getInstance().getConsole().warning("Extracting mailboxes.db file..."); + LTItemMail.getInstance().saveResource("mailboxes.db", false); + LTItemMail.getInstance().getConsole().info("Done."); + } else LTItemMail.getInstance().getConsole().info("Found mailboxes.db file."); + } + public static final Connection load() { + final File databaseFile = new File(LTItemMail.getInstance().getDataFolder(), "mailboxes.db"); + if(databaseFile.exists()) { + try { + final Connection con = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getCanonicalPath()); + return con; + } catch (final SQLException | IOException e) { + e.printStackTrace(); + } + LTItemMail.getInstance().getConsole().severe("A database error occurred."); + return null; + } + LTItemMail.getInstance().getConsole().severe("Missing mailboxes.db file."); + return null; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/module/LanguageModule.java b/src/main/java/io/github/leothawne/LTItemMail/module/LanguageModule.java index 482cc5f..4e89e19 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/module/LanguageModule.java +++ b/src/main/java/io/github/leothawne/LTItemMail/module/LanguageModule.java @@ -11,49 +11,35 @@ import io.github.leothawne.LTItemMail.type.VersionType; public final class LanguageModule { - public static final void check(final LTItemMail plugin, final ConsoleModule console, final FileConfiguration configuration) { - final File languageFile = new File(plugin.getDataFolder(), configuration.getString("language") + ".yml"); + public static final void check() { + final File languageFile = new File(LTItemMail.getInstance().getDataFolder(), LTItemMail.getInstance().getConfiguration().getString("language") + ".yml"); if(!languageFile.exists()) { - console.warning("Extracting " + configuration.getString("language") + ".yml file..."); - if(configuration.getString("language").equalsIgnoreCase("vietnamese") || configuration.getString("language").equalsIgnoreCase("english") || configuration.getString("language").equalsIgnoreCase("portuguese")) { - plugin.saveResource(configuration.getString("language") + ".yml", false); - console.info("Done."); - } else { - console.warning(configuration.getString("language") + ".yml file is not supported yet. I suggest you to manually create the language file and do manually the translation."); - } - } else { - console.info("Found " + configuration.getString("language") + ".yml file."); - } + LTItemMail.getInstance().getConsole().warning("Extracting " + LTItemMail.getInstance().getConfiguration().getString("language") + ".yml file..."); + if(LTItemMail.getInstance().getConfiguration().getString("language").equalsIgnoreCase("vietnamese") || LTItemMail.getInstance().getConfiguration().getString("language").equalsIgnoreCase("english") || LTItemMail.getInstance().getConfiguration().getString("language").equalsIgnoreCase("portuguese")) { + LTItemMail.getInstance().saveResource(LTItemMail.getInstance().getConfiguration().getString("language") + ".yml", false); + LTItemMail.getInstance().getConsole().info("Done."); + } else LTItemMail.getInstance().getConsole().warning(LTItemMail.getInstance().getConfiguration().getString("language") + ".yml file is not supported yet. I suggest you to manually create the language file and do manually the translation."); + } else LTItemMail.getInstance().getConsole().info("Found " + LTItemMail.getInstance().getConfiguration().getString("language") + ".yml file."); } - public static final FileConfiguration load(final LTItemMail plugin, final ConsoleModule console, final FileConfiguration configuration) { - final File languageFile = new File(plugin.getDataFolder(), configuration.getString("language") + ".yml"); + public static final FileConfiguration load() { + final File languageFile = new File(LTItemMail.getInstance().getDataFolder(), LTItemMail.getInstance().getConfiguration().getString("language") + ".yml"); if(languageFile.exists()) { final FileConfiguration languageConfig = new YamlConfiguration(); try { languageConfig.load(languageFile); - console.info("Loaded " + configuration.getString("language") + ".yml file."); + LTItemMail.getInstance().getConsole().info("Loaded " + LTItemMail.getInstance().getConfiguration().getString("language") + ".yml file."); int languageVersion = 0; - if(configuration.getString("language").equalsIgnoreCase("english")) { - languageVersion = Integer.parseInt(DataModule.getVersion(VersionType.ENGLISH_YML)); - } - if(configuration.getString("language").equalsIgnoreCase("portuguese")) { - languageVersion = Integer.parseInt(DataModule.getVersion(VersionType.PORTUGUESE_YML)); - } - if(configuration.getString("language").equalsIgnoreCase("vietnamese")) { - languageVersion = Integer.parseInt(DataModule.getVersion(VersionType.VIETNAMESE_YML)); - } - if(languageVersion != 0) { - if(languageConfig.getInt("language-version") != languageVersion) { - console.severe(configuration.getString("language") + ".yml file outdated. Delete that file and restart the server."); - } - } + if(LTItemMail.getInstance().getConfiguration().getString("language").equalsIgnoreCase("english")) languageVersion = Integer.parseInt(DataModule.getVersion(VersionType.ENGLISH_YML)); + if(LTItemMail.getInstance().getConfiguration().getString("language").equalsIgnoreCase("portuguese")) languageVersion = Integer.parseInt(DataModule.getVersion(VersionType.PORTUGUESE_YML)); + if(LTItemMail.getInstance().getConfiguration().getString("language").equalsIgnoreCase("vietnamese")) languageVersion = Integer.parseInt(DataModule.getVersion(VersionType.VIETNAMESE_YML)); + if(languageVersion != 0) if(languageConfig.getInt("language-version") != languageVersion) LTItemMail.getInstance().getConsole().severe(LTItemMail.getInstance().getConfiguration().getString("language") + ".yml file outdated. Delete that file and restart the server."); return languageConfig; } catch(final IOException | InvalidConfigurationException exception) { exception.printStackTrace(); } return null; } - console.severe("Missing " + configuration.getString("language") + ".yml file."); + LTItemMail.getInstance().getConsole().severe("Missing " + LTItemMail.getInstance().getConfiguration().getString("language") + ".yml file."); return null; } } \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/module/MetricsModule.java b/src/main/java/io/github/leothawne/LTItemMail/module/MetricsModule.java index 07c83b5..d37c1f8 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/module/MetricsModule.java +++ b/src/main/java/io/github/leothawne/LTItemMail/module/MetricsModule.java @@ -4,13 +4,8 @@ import io.github.leothawne.LTItemMail.api.MetricsAPI; public final class MetricsModule { - public static final MetricsAPI init(final LTItemMail plugin, final ConsoleModule console) { - final MetricsAPI metrics = new MetricsAPI(plugin, 3647); - if(metrics.isEnabled()) { - console.info(plugin.getName() + " is using bStats: https://bstats.org/getting-started"); - } else { - console.warning("bStats is disabled. Please enable bStats!"); - } + public static final MetricsAPI init() { + final MetricsAPI metrics = new MetricsAPI(LTItemMail.getInstance(), 3647); return metrics; } } \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/module/VaultModule.java b/src/main/java/io/github/leothawne/LTItemMail/module/VaultModule.java index 9667a89..7cc6a6b 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/module/VaultModule.java +++ b/src/main/java/io/github/leothawne/LTItemMail/module/VaultModule.java @@ -1,21 +1,19 @@ package io.github.leothawne.LTItemMail.module; +import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.RegisteredServiceProvider; -import io.github.leothawne.LTItemMail.LTItemMail; import net.milkbowl.vault.economy.Economy; public final class VaultModule { - public static final boolean isVaultInstalled(final LTItemMail plugin) { - final Plugin vault = plugin.getServer().getPluginManager().getPlugin("Vault"); + public static final boolean isVaultInstalled() { + final Plugin vault = Bukkit.getPluginManager().getPlugin("Vault"); return (vault != null && vault.isEnabled()); } - public static final Economy getEconomy(final LTItemMail plugin) { - final RegisteredServiceProvider rsp = plugin.getServer().getServicesManager().getRegistration(Economy.class); - if(rsp != null) { - return rsp.getProvider(); - } + public static final Economy getEconomy() { + final RegisteredServiceProvider rsp = Bukkit.getServicesManager().getRegistration(Economy.class); + if(rsp != null) return rsp.getProvider(); return null; } } \ No newline at end of file diff --git a/src/main/java/io/github/leothawne/LTItemMail/task/VersionTask.java b/src/main/java/io/github/leothawne/LTItemMail/task/VersionTask.java index b99fb66..1da5461 100644 --- a/src/main/java/io/github/leothawne/LTItemMail/task/VersionTask.java +++ b/src/main/java/io/github/leothawne/LTItemMail/task/VersionTask.java @@ -1,31 +1,25 @@ package io.github.leothawne.LTItemMail.task; +import org.bukkit.Bukkit; + import io.github.leothawne.LTItemMail.LTItemMail; import io.github.leothawne.LTItemMail.api.HTTP; -import io.github.leothawne.LTItemMail.module.ConsoleModule; import io.github.leothawne.LTItemMail.module.DataModule; import io.github.leothawne.LTItemMail.type.ProjectPageType; public final class VersionTask implements Runnable { - private static LTItemMail plugin; - private static ConsoleModule myLogger; - public VersionTask(final LTItemMail plugin, final ConsoleModule myLogger) { - VersionTask.plugin = plugin; - VersionTask.myLogger = myLogger; - } + public VersionTask() {} @Override public final void run() { - final String version = VersionTask.plugin.getDescription().getVersion(); + final String version = LTItemMail.getInstance().getDescription().getVersion(); final String url = DataModule.getPluginURL(version); final String response = HTTP.getData(url); if(response != null) { if(response.equalsIgnoreCase("disabled")) { - VersionTask.myLogger.info("Hey you! Stop right there! This version (" + version + ") is no longer allowed to be used/played."); - VersionTask.myLogger.info("Download a newer version: " + DataModule.getProjectPage(ProjectPageType.BUKKIT_DEV) + " or " + DataModule.getProjectPage(ProjectPageType.SPIGOT_MC)); - VersionTask.plugin.getServer().getPluginManager().disablePlugin(VersionTask.plugin); + LTItemMail.getInstance().getConsole().info("Hey you! Stop right there! This version (" + version + ") is no longer allowed to be used/played."); + LTItemMail.getInstance().getConsole().info("Download a newer version: " + DataModule.getProjectPage(ProjectPageType.BUKKIT_DEV) + " or " + DataModule.getProjectPage(ProjectPageType.SPIGOT_MC)); + Bukkit.getPluginManager().disablePlugin(LTItemMail.getInstance()); } - } else { - VersionTask.myLogger.warning("Unable to locate: " + url); - } + } else LTItemMail.getInstance().getConsole().warning("Unable to locate: " + url); } } \ No newline at end of file