diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..199f31b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +out/ +target/ +StaffChat.iml diff --git a/pom.xml b/pom.xml index 1685a33..baf5d48 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ me.oskar3123 staffchat - 1.1.2-SNAPSHOT + 1.1.3-SNAPSHOT @@ -24,17 +24,17 @@ org.spigotmc spigot-api - 1.11.2-R0.1-SNAPSHOT + 1.12.2-R0.1-SNAPSHOT org.bukkit bukkit - 1.11.2-R0.1-SNAPSHOT + 1.12.2-R0.1-SNAPSHOT net.md-5 bungeecord-api - 1.11-SNAPSHOT + 1.12-SNAPSHOT diff --git a/src/main/java/me/oskar3123/staffchat/bungee/BungeeMain.java b/src/main/java/me/oskar3123/staffchat/bungee/BungeeMain.java index f646c6b..0d2900b 100644 --- a/src/main/java/me/oskar3123/staffchat/bungee/BungeeMain.java +++ b/src/main/java/me/oskar3123/staffchat/bungee/BungeeMain.java @@ -1,7 +1,7 @@ package me.oskar3123.staffchat.bungee; import me.oskar3123.staffchat.bungee.command.BungeeStaffChatCommand; -import me.oskar3123.staffchat.bungee.event.BungeeChatListener; +import me.oskar3123.staffchat.bungee.listener.BungeeChatListener; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.config.Configuration; import net.md_5.bungee.config.ConfigurationProvider; diff --git a/src/main/java/me/oskar3123/staffchat/bungee/MetricsLite.java b/src/main/java/me/oskar3123/staffchat/bungee/MetricsLite.java index ca3286f..1e6bc74 100644 --- a/src/main/java/me/oskar3123/staffchat/bungee/MetricsLite.java +++ b/src/main/java/me/oskar3123/staffchat/bungee/MetricsLite.java @@ -9,96 +9,96 @@ import net.md_5.bungee.config.ConfigurationProvider; import net.md_5.bungee.config.YamlConfiguration; import javax.net.ssl.HttpsURLConnection; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; +import java.io.*; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.zip.GZIPOutputStream; /** * bStats collects some data for plugin authors. - * + *

* Check out https://bStats.org/ to learn more about bStats! */ @SuppressWarnings("all") -public class MetricsLite { +public class MetricsLite +{ - static { + // The version of this bStats class + public static final int B_STATS_VERSION = 1; + // The url to which the data is sent + private static final String URL = "https://bStats.org/submitData/bungeecord"; + // A list with all known metrics class objects including this one + private static final List knownMetricsInstances = new ArrayList<>(); + + static + { // 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' }); + 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 nobody just copy & pastes the example and use the wrong package names - if (MetricsLite.class.getPackage().getName().equals(defaultPackage) || MetricsLite.class.getPackage().getName().equals(examplePackage)) { + if (MetricsLite.class.getPackage().getName().equals(defaultPackage) || MetricsLite.class.getPackage().getName().equals(examplePackage)) + { throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); } } - // The version of this bStats class - public static final int B_STATS_VERSION = 1; - - // The url to which the data is sent - private static final String URL = "https://bStats.org/submitData/bungeecord"; - // The plugin private final Plugin plugin; - // Is bStats enabled on this server? private boolean enabled; - // The uuid of the server private String serverUUID; - // Should failed requests be logged? private boolean logFailedRequests = false; - // A list with all known metrics class objects including this one - private static final List knownMetricsInstances = new ArrayList<>(); - - public MetricsLite(Plugin plugin) { + public MetricsLite(Plugin plugin) + { this.plugin = plugin; - try { + try + { loadConfig(); - } catch (IOException e) { + } + catch (IOException e) + { // Failed to load configuration plugin.getLogger().log(Level.WARNING, "Failed to load bStats config!", e); return; } // We are not allowed to send data about this server :( - if (!enabled) { + if (!enabled) + { return; } Class usedMetricsClass = getFirstBStatsClass(); - if (usedMetricsClass == null) { + if (usedMetricsClass == null) + { // Failed to get first metrics class return; } - if (usedMetricsClass == getClass()) { + if (usedMetricsClass == getClass()) + { // We are the first! :) linkMetrics(this); startSubmitting(); - } else { + } + else + { // We aren't the first so we link to the first metrics class - try { - usedMetricsClass.getMethod("linkMetrics", Object.class).invoke(null,this); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - if (logFailedRequests) { + try + { + usedMetricsClass.getMethod("linkMetrics", Object.class).invoke(null, this); + } + catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) + { + if (logFailedRequests) + { plugin.getLogger().log(Level.WARNING, "Failed to link to first metrics class " + usedMetricsClass.getName() + "!", e); } } @@ -111,17 +111,76 @@ public class MetricsLite { * * @param metrics An object of the metrics class to link. */ - public static void linkMetrics(Object metrics) { + public static void linkMetrics(Object metrics) + { knownMetricsInstances.add(metrics); } + /** + * Sends the data to the bStats server. + * + * @param data The data to send. + * @throws Exception If the request failed. + */ + private static void sendData(JsonObject data) throws Exception + { + if (data == null) + { + throw new IllegalArgumentException("Data cannot be null"); + } + + 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); + DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.write(compressedData); + outputStream.flush(); + outputStream.close(); + + connection.getInputStream().close(); // We don't care about the response - Just send our data :) + } + + /** + * Gzips the given String. + * + * @param str The string to gzip. + * @return The gzipped String. + * @throws IOException If the compression failed. + */ + private static byte[] compress(final String str) throws IOException + { + if (str == null) + { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(outputStream); + gzip.write(str.getBytes("UTF-8")); + gzip.close(); + return outputStream.toByteArray(); + } + /** * Gets the plugin specific data. * This method is called using Reflection. * * @return The plugin specific data. */ - public JsonObject getPluginData() { + public JsonObject getPluginData() + { JsonObject data = new JsonObject(); String pluginName = plugin.getDescription().getName(); @@ -136,23 +195,28 @@ public class MetricsLite { return data; } - private void startSubmitting() { + private void startSubmitting() + { // We use a timer cause want to be independent from the server tps final Timer timer = new Timer(true); - timer.scheduleAtFixedRate(new TimerTask() { + timer.scheduleAtFixedRate(new TimerTask() + { @Override - public void run() { + public void run() + { // The data collection (e.g. for custom graphs) is done sync // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;) TaskScheduler scheduler = plugin.getProxy().getScheduler(); - scheduler.schedule(plugin, new Runnable() { + scheduler.schedule(plugin, new Runnable() + { @Override - public void run() { + public void run() + { submitData(); } }, 0L, TimeUnit.SECONDS); } - }, 1000*60*2, 1000*60*30); + }, 1000 * 60 * 2, 1000 * 60 * 30); // Submit the data every 30 minutes, first time after 2 minutes to give other plugins enough time to start // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! // WARNING: Just don't do it! @@ -163,7 +227,8 @@ public class MetricsLite { * * @return The server specific data. */ - private JsonObject getServerData() { + private JsonObject getServerData() + { // Minecraft specific data int playerAmount = plugin.getProxy().getOnlineCount(); playerAmount = playerAmount > 500 ? 500 : playerAmount; @@ -199,32 +264,45 @@ public class MetricsLite { /** * Collects the data and sends it afterwards. */ - private void submitData() { + private void submitData() + { final JsonObject data = getServerData(); final JsonArray pluginData = new JsonArray(); // Search for all other bStats Metrics classes to get their plugin data - for (Object metrics : knownMetricsInstances) { - try { + for (Object metrics : knownMetricsInstances) + { + try + { Object plugin = metrics.getClass().getMethod("getPluginData").invoke(metrics); - if (plugin instanceof JsonObject) { + if (plugin instanceof JsonObject) + { pluginData.add((JsonObject) plugin); } - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } + } + catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) + { + } } data.add("plugins", pluginData); // Create a new thread for the connection to the bStats server - new Thread(new Runnable() { + new Thread(new Runnable() + { @Override - public void run() { - try { + public void run() + { + try + { // Send the data sendData(data); - } catch (Exception e) { + } + catch (Exception e) + { // Something went wrong! :( - if (logFailedRequests) { + if (logFailedRequests) + { plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats!", e); } } @@ -237,11 +315,13 @@ public class MetricsLite { * * @throws IOException If something did not work :( */ - private void loadConfig() throws IOException { + private void loadConfig() throws IOException + { Path configPath = plugin.getDataFolder().toPath().getParent().resolve("bStats"); configPath.toFile().mkdirs(); File configFile = new File(configPath.toFile(), "config.yml"); - if (!configFile.exists()) { + if (!configFile.exists()) + { writeFile(configFile, "#bStats collects some data for plugin authors like how many servers are using their plugins.", "#To honor their work, you should not disable it.", @@ -265,23 +345,33 @@ public class MetricsLite { * * @return The first bStats metrics class. */ - private Class getFirstBStatsClass() { + private Class getFirstBStatsClass() + { Path configPath = plugin.getDataFolder().toPath().getParent().resolve("bStats"); configPath.toFile().mkdirs(); File tempFile = new File(configPath.toFile(), "temp.txt"); - try { + try + { String className = readFile(tempFile); - if (className != null) { - try { + if (className != null) + { + try + { // Let's check if a class with the given name exists. return Class.forName(className); - } catch (ClassNotFoundException ignored) { } + } + catch (ClassNotFoundException ignored) + { + } } writeFile(tempFile, getClass().getName()); return getClass(); - } catch (IOException e) { - if (logFailedRequests) { + } + catch (IOException e) + { + if (logFailedRequests) + { plugin.getLogger().log(Level.WARNING, "Failed to get first bStats class!", e); } return null; @@ -295,14 +385,17 @@ public class MetricsLite { * @return The first line of the file or null if the file does not exist or is empty. * @throws IOException If something did not work :( */ - private String readFile(File file) throws IOException { - if (!file.exists()) { + private String readFile(File file) throws IOException + { + if (!file.exists()) + { return null; } try ( FileReader fileReader = new FileReader(file); - BufferedReader bufferedReader = new BufferedReader(fileReader); - ) { + BufferedReader bufferedReader = new BufferedReader(fileReader); + ) + { return bufferedReader.readLine(); } } @@ -310,76 +403,27 @@ public class MetricsLite { /** * Writes a String to a file. It also adds a note for the user, * - * @param file The file to write to. Cannot be null. + * @param file The file to write to. Cannot be null. * @param lines The lines to write. * @throws IOException If something did not work :( */ - private void writeFile(File file, String... lines) throws IOException { - if (!file.exists()) { + private void writeFile(File file, String... lines) throws IOException + { + if (!file.exists()) + { file.createNewFile(); } try ( FileWriter fileWriter = new FileWriter(file); BufferedWriter bufferedWriter = new BufferedWriter(fileWriter) - ) { - for (String line : lines) { + ) + { + for (String line : lines) + { bufferedWriter.write(line); bufferedWriter.newLine(); } } } - /** - * Sends the data to the bStats server. - * - * @param data The data to send. - * @throws Exception If the request failed. - */ - private static void sendData(JsonObject data) throws Exception { - if (data == null) { - throw new IllegalArgumentException("Data cannot be null"); - } - - 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); - DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); - outputStream.write(compressedData); - outputStream.flush(); - outputStream.close(); - - connection.getInputStream().close(); // We don't care about the response - Just send our data :) - } - - /** - * Gzips the given String. - * - * @param str The string to gzip. - * @return The gzipped String. - * @throws IOException If the compression failed. - */ - private static byte[] compress(final String str) throws IOException { - if (str == null) { - return null; - } - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - GZIPOutputStream gzip = new GZIPOutputStream(outputStream); - gzip.write(str.getBytes("UTF-8")); - gzip.close(); - return outputStream.toByteArray(); - } - } \ No newline at end of file diff --git a/src/main/java/me/oskar3123/staffchat/bungee/event/BungeeStaffChatEvent.java b/src/main/java/me/oskar3123/staffchat/bungee/event/BungeeStaffChatEvent.java new file mode 100644 index 0000000..6d5f259 --- /dev/null +++ b/src/main/java/me/oskar3123/staffchat/bungee/event/BungeeStaffChatEvent.java @@ -0,0 +1,54 @@ +package me.oskar3123.staffchat.bungee.event; + +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Cancellable; +import net.md_5.bungee.api.plugin.Event; + +public class BungeeStaffChatEvent extends Event implements Cancellable +{ + + private final String message; + private final ProxiedPlayer player; + private boolean cancelled = false; + private String format; + + public BungeeStaffChatEvent(ProxiedPlayer player, String format, String message) + { + this.player = player; + this.format = format; + this.message = message; + } + + public String getFormat() + { + return format; + } + + public void setFormat(String format) + { + this.format = format; + } + + public String getMessage() + { + return message; + } + + public ProxiedPlayer getPlayer() + { + return player; + } + + @Override + public boolean isCancelled() + { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) + { + this.cancelled = cancelled; + } + +} diff --git a/src/main/java/me/oskar3123/staffchat/bungee/event/BungeeChatListener.java b/src/main/java/me/oskar3123/staffchat/bungee/listener/BungeeChatListener.java similarity index 63% rename from src/main/java/me/oskar3123/staffchat/bungee/event/BungeeChatListener.java rename to src/main/java/me/oskar3123/staffchat/bungee/listener/BungeeChatListener.java index a947e48..16e01d9 100644 --- a/src/main/java/me/oskar3123/staffchat/bungee/event/BungeeChatListener.java +++ b/src/main/java/me/oskar3123/staffchat/bungee/listener/BungeeChatListener.java @@ -1,6 +1,7 @@ -package me.oskar3123.staffchat.bungee.event; +package me.oskar3123.staffchat.bungee.listener; import me.oskar3123.staffchat.bungee.BungeeMain; +import me.oskar3123.staffchat.bungee.event.BungeeStaffChatEvent; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; @@ -42,13 +43,23 @@ public class BungeeChatListener implements Listener } String format = config.getString("settings.format"); - format = format.replaceAll("\\{NAME\\}", player.getName()); - format = format.replaceAll("\\{MESSAGE\\}", event.getMessage().substring(character.length()).trim()); - final BaseComponent[] message = txt(format); + String message = event.getMessage().substring(character.length()).trim(); + + BungeeStaffChatEvent chatEvent = new BungeeStaffChatEvent(player, format, message); + plugin.getProxy().getPluginManager().callEvent(chatEvent); + if (chatEvent.isCancelled()) + { + return; + } + format = chatEvent.getFormat(); + + format = format.replaceAll("\\{NAME\\}", sanitize(player.getName())); + format = format.replaceAll("\\{MESSAGE\\}", sanitize(message)); + final BaseComponent[] messageComponents = txt(format); plugin.getProxy().getPlayers().stream() .filter(p -> p.hasPermission(plugin.seePerm)) - .forEach(p -> p.sendMessage(message)); + .forEach(p -> p.sendMessage(messageComponents)); plugin.getLogger().info(ChatColor.stripColor(clr(format))); event.setCancelled(true); @@ -64,4 +75,11 @@ public class BungeeChatListener implements Listener return TextComponent.fromLegacyText(clr(text)); } + private String sanitize(String string) + { + string = string.replaceAll("\\\\", "\\\\\\\\"); + string = string.replaceAll("\\$", "\\\\\\$"); + return string; + } + } diff --git a/src/main/java/me/oskar3123/staffchat/spigot/Main.java b/src/main/java/me/oskar3123/staffchat/spigot/Main.java index 334749a..bbc28a6 100644 --- a/src/main/java/me/oskar3123/staffchat/spigot/Main.java +++ b/src/main/java/me/oskar3123/staffchat/spigot/Main.java @@ -1,7 +1,7 @@ package me.oskar3123.staffchat.spigot; import me.oskar3123.staffchat.spigot.command.StaffChatCommand; -import me.oskar3123.staffchat.spigot.event.ChatListener; +import me.oskar3123.staffchat.spigot.listener.ChatListener; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; diff --git a/src/main/java/me/oskar3123/staffchat/spigot/MetricsLite.java b/src/main/java/me/oskar3123/staffchat/spigot/MetricsLite.java index 4d1ff05..483165d 100644 --- a/src/main/java/me/oskar3123/staffchat/spigot/MetricsLite.java +++ b/src/main/java/me/oskar3123/staffchat/spigot/MetricsLite.java @@ -22,34 +22,34 @@ import java.util.zip.GZIPOutputStream; /** * bStats collects some data for plugin authors. - * + *

* Check out https://bStats.org/ to learn more about bStats! */ @SuppressWarnings("all") -public class MetricsLite { - - static { - // 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 nobody just copy & pastes the example and use the wrong package names - if (MetricsLite.class.getPackage().getName().equals(defaultPackage) || MetricsLite.class.getPackage().getName().equals(examplePackage)) { - throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); - } - } +public class MetricsLite +{ // The version of this bStats class public static final int B_STATS_VERSION = 1; - // The url to which the data is sent private static final String URL = "https://bStats.org/submitData/bukkit"; - // Should failed requests be logged? private static boolean logFailedRequests; - // The uuid of the server private static String serverUUID; + static + { + // 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 nobody just copy & pastes the example and use the wrong package names + if (MetricsLite.class.getPackage().getName().equals(defaultPackage) || MetricsLite.class.getPackage().getName().equals(examplePackage)) + { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + // The plugin private final JavaPlugin plugin; @@ -58,8 +58,10 @@ public class MetricsLite { * * @param plugin The plugin which stats should be submitted. */ - public MetricsLite(JavaPlugin plugin) { - if (plugin == null) { + public MetricsLite(JavaPlugin plugin) + { + if (plugin == null) + { throw new IllegalArgumentException("Plugin cannot be null!"); } this.plugin = plugin; @@ -70,7 +72,8 @@ public class MetricsLite { YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); // Check if the config file exists - if (!config.isSet("serverUuid")) { + if (!config.isSet("serverUuid")) + { // Add default values config.addDefault("enabled", true); @@ -86,166 +89,58 @@ public class MetricsLite { "This has nearly no effect on the server performance!\n" + "Check out https://bStats.org/ to learn more :)" ).copyDefaults(true); - try { + try + { config.save(configFile); - } catch (IOException ignored) { } + } + catch (IOException ignored) + { + } } // Load the data serverUUID = config.getString("serverUuid"); logFailedRequests = config.getBoolean("logFailedRequests", false); - if (config.getBoolean("enabled", true)) { + if (config.getBoolean("enabled", true)) + { 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 { + 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) { } + } + catch (NoSuchFieldException ignored) + { + } } // Register our service Bukkit.getServicesManager().register(MetricsLite.class, this, plugin, ServicePriority.Normal); - if (!found) { + if (!found) + { // We are the first! startSubmitting(); } } } - /** - * Starts the Scheduler which submits our data every 30 minutes. - */ - private void startSubmitting() { - final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags - timer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - if (!plugin.isEnabled()) { // Plugin was disabled - timer.cancel(); - 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, new Runnable() { - @Override - public void run() { - submitData(); - } - }); - } - }, 1000*60*5, 1000*60*30); - // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start - // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! - // WARNING: Just don't do it! - } - - /** - * Gets the plugin specific data. - * This method is called using Reflection. - * - * @return The plugin specific data. - */ - public JSONObject getPluginData() { - JSONObject data = new JSONObject(); - - String pluginName = plugin.getDescription().getName(); - String pluginVersion = plugin.getDescription().getVersion(); - - data.put("pluginName", pluginName); // Append the name of the plugin - data.put("pluginVersion", pluginVersion); // Append the version of the plugin - JSONArray customCharts = new JSONArray(); - data.put("customCharts", customCharts); - - return data; - } - - /** - * Gets the server specific data. - * - * @return The server specific data. - */ - private JSONObject getServerData() { - // Minecraft specific data - int playerAmount = Bukkit.getOnlinePlayers().size(); - int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; - String bukkitVersion = Bukkit.getVersion(); - bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1); - - // 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(); - - JSONObject data = new JSONObject(); - - data.put("serverUUID", serverUUID); - - data.put("playerAmount", playerAmount); - data.put("onlineMode", onlineMode); - data.put("bukkitVersion", bukkitVersion); - - data.put("javaVersion", javaVersion); - data.put("osName", osName); - data.put("osArch", osArch); - data.put("osVersion", osVersion); - data.put("coreCount", coreCount); - - return data; - } - - /** - * Collects the data and sends it afterwards. - */ - private void submitData() { - final JSONObject data = getServerData(); - - 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 :) - } catch (NoSuchFieldException ignored) { - continue; // Continue "searching" - } - // Found one! - try { - pluginData.add(service.getMethod("getPluginData").invoke(Bukkit.getServicesManager().load(service))); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } - } - - data.put("plugins", pluginData); - - // Create a new thread for the connection to the bStats server - new Thread(new Runnable() { - @Override - public void run() { - try { - // Send the data - sendData(data); - } catch (Exception e) { - // Something went wrong! :( - if (logFailedRequests) { - plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); - } - } - } - }).start(); - } - /** * Sends the data to the bStats server. * * @param data The data to send. * @throws Exception If the request failed. */ - private static void sendData(JSONObject data) throws Exception { - if (data == null) { + private static void sendData(JSONObject data) throws Exception + { + if (data == null) + { throw new IllegalArgumentException("Data cannot be null!"); } - if (Bukkit.isPrimaryThread()) { + if (Bukkit.isPrimaryThread()) + { throw new IllegalAccessException("This method must not be called from the main thread!"); } HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); @@ -279,8 +174,10 @@ public class MetricsLite { * @return The gzipped String. * @throws IOException If the compression failed. */ - private static byte[] compress(final String str) throws IOException { - if (str == null) { + private static byte[] compress(final String str) throws IOException + { + if (str == null) + { return null; } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -290,4 +187,149 @@ public class MetricsLite { return outputStream.toByteArray(); } + /** + * Starts the Scheduler which submits our data every 30 minutes. + */ + private void startSubmitting() + { + final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags + timer.scheduleAtFixedRate(new TimerTask() + { + @Override + public void run() + { + if (!plugin.isEnabled()) + { // Plugin was disabled + timer.cancel(); + 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, new Runnable() + { + @Override + public void run() + { + submitData(); + } + }); + } + }, 1000 * 60 * 5, 1000 * 60 * 30); + // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start + // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! + // WARNING: Just don't do it! + } + + /** + * Gets the plugin specific data. + * This method is called using Reflection. + * + * @return The plugin specific data. + */ + public JSONObject getPluginData() + { + JSONObject data = new JSONObject(); + + String pluginName = plugin.getDescription().getName(); + String pluginVersion = plugin.getDescription().getVersion(); + + data.put("pluginName", pluginName); // Append the name of the plugin + data.put("pluginVersion", pluginVersion); // Append the version of the plugin + JSONArray customCharts = new JSONArray(); + data.put("customCharts", customCharts); + + return data; + } + + /** + * Gets the server specific data. + * + * @return The server specific data. + */ + private JSONObject getServerData() + { + // Minecraft specific data + int playerAmount = Bukkit.getOnlinePlayers().size(); + int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; + String bukkitVersion = Bukkit.getVersion(); + bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1); + + // 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(); + + JSONObject data = new JSONObject(); + + data.put("serverUUID", serverUUID); + + data.put("playerAmount", playerAmount); + data.put("onlineMode", onlineMode); + data.put("bukkitVersion", bukkitVersion); + + data.put("javaVersion", javaVersion); + data.put("osName", osName); + data.put("osArch", osArch); + data.put("osVersion", osVersion); + data.put("coreCount", coreCount); + + return data; + } + + /** + * Collects the data and sends it afterwards. + */ + private void submitData() + { + final JSONObject data = getServerData(); + + 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 :) + } + catch (NoSuchFieldException ignored) + { + continue; // Continue "searching" + } + // Found one! + try + { + pluginData.add(service.getMethod("getPluginData").invoke(Bukkit.getServicesManager().load(service))); + } + catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) + { + } + } + + data.put("plugins", pluginData); + + // Create a new thread for the connection to the bStats server + new Thread(new Runnable() + { + @Override + public void run() + { + try + { + // Send the data + sendData(data); + } + catch (Exception e) + { + // Something went wrong! :( + if (logFailedRequests) + { + plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); + } + } + } + }).start(); + } + } \ No newline at end of file diff --git a/src/main/java/me/oskar3123/staffchat/spigot/event/StaffChatEvent.java b/src/main/java/me/oskar3123/staffchat/spigot/event/StaffChatEvent.java new file mode 100644 index 0000000..a16f25e --- /dev/null +++ b/src/main/java/me/oskar3123/staffchat/spigot/event/StaffChatEvent.java @@ -0,0 +1,67 @@ +package me.oskar3123.staffchat.spigot.event; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class StaffChatEvent extends Event implements Cancellable +{ + + private static HandlerList handlerList = new HandlerList(); + private final Player player; + private final String message; + private boolean cancelled = false; + private String format; + + public StaffChatEvent(Player player, String format, String message) + { + this.player = player; + this.format = format; + this.message = message; + } + + public static HandlerList getHandlerList() + { + return handlerList; + } + + public String getFormat() + { + return format; + } + + public void setFormat(String format) + { + this.format = format; + } + + public Player getPlayer() + { + return player; + } + + public String getMessage() + { + return message; + } + + @Override + public HandlerList getHandlers() + { + return handlerList; + } + + @Override + public boolean isCancelled() + { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) + { + this.cancelled = cancelled; + } + +} diff --git a/src/main/java/me/oskar3123/staffchat/spigot/event/ChatListener.java b/src/main/java/me/oskar3123/staffchat/spigot/listener/ChatListener.java similarity index 52% rename from src/main/java/me/oskar3123/staffchat/spigot/event/ChatListener.java rename to src/main/java/me/oskar3123/staffchat/spigot/listener/ChatListener.java index bc62382..89f8df4 100644 --- a/src/main/java/me/oskar3123/staffchat/spigot/event/ChatListener.java +++ b/src/main/java/me/oskar3123/staffchat/spigot/listener/ChatListener.java @@ -1,11 +1,11 @@ -package me.oskar3123.staffchat.spigot.event; +package me.oskar3123.staffchat.spigot.listener; import me.oskar3123.staffchat.spigot.Main; +import me.oskar3123.staffchat.spigot.event.StaffChatEvent; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.AsyncPlayerChatEvent; @@ -19,7 +19,7 @@ public class ChatListener implements Listener this.plugin = plugin; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + @EventHandler public void chat(AsyncPlayerChatEvent event) { if (!event.getPlayer().hasPermission(plugin.usePerm)) @@ -35,17 +35,34 @@ public class ChatListener implements Listener } String format = config.getString("settings.format"); - format = format.replaceAll("\\{NAME\\}", event.getPlayer().getName()); - format = format.replaceAll("\\{MESSAGE\\}", event.getMessage().substring(character.length()).trim()); + String message = event.getMessage().substring(character.length()).trim(); + + StaffChatEvent chatEvent = new StaffChatEvent(event.getPlayer(), format, message); + Bukkit.getServer().getPluginManager().callEvent(chatEvent); + if (chatEvent.isCancelled()) + { + return; + } + format = chatEvent.getFormat(); + + format = format.replaceAll("\\{NAME\\}", sanitize(event.getPlayer().getName())); + format = format.replaceAll("\\{MESSAGE\\}", sanitize(message)); format = ChatColor.translateAlternateColorCodes('&', format); - final String message = format; + final String finalMessage = format; Bukkit.getOnlinePlayers().stream() .filter(p -> p.hasPermission(plugin.seePerm)) - .forEach(p -> p.sendMessage(message)); - plugin.getLogger().info(ChatColor.stripColor(message)); + .forEach(p -> p.sendMessage(finalMessage)); + plugin.getLogger().info(ChatColor.stripColor(finalMessage)); event.setCancelled(true); } + private String sanitize(String string) + { + string = string.replaceAll("\\\\", "\\\\\\\\"); + string = string.replaceAll("\\$", "\\\\\\$"); + return string; + } + } diff --git a/src/main/resources/bungee.yml b/src/main/resources/bungee.yml index 5a046ee..392eaf9 100644 --- a/src/main/resources/bungee.yml +++ b/src/main/resources/bungee.yml @@ -1,4 +1,4 @@ name: StaffChat authors: [oskar3123] main: me.oskar3123.staffchat.bungee.BungeeMain -version: 1.1.2 +version: 1.1.3 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 06b78ab..15425da 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ name: StaffChat authors: [oskar3123] main: me.oskar3123.staffchat.spigot.Main -version: 1.1.2 +version: 1.1.3 commands: staffchat: