replyRecipient;
@@ -42,7 +41,7 @@ public class SimpleMessageRecipient implements IMessageRecipient {
}
return recipient instanceof User ? (User) recipient : null;
}
-
+
public SimpleMessageRecipient(IEssentials ess, IMessageRecipient parent) {
this.ess = ess;
this.parent = parent;
@@ -117,13 +116,12 @@ public class SimpleMessageRecipient implements IMessageRecipient {
if (!isReachable()) {
return MessageResponse.UNREACHABLE;
}
-
+
User user = getUser(this);
boolean afk = false;
if (user != null) {
- if (user.isIgnoreMsg()
- && !(sender instanceof Console)) { // Console must never be ignored.
- return MessageResponse.MESSAGES_IGNORED;
+ if (user.isIgnoreMsg() && sender instanceof IUser && !((IUser) sender).isAuthorized("essentials.msgtoggle.bypass")) { // Don't ignore console and senders with permission
+ return MessageResponse.MESSAGES_IGNORED;
}
afk = user.isAfk();
// Check whether this recipient ignores the sender, only if the sender is not the console.
@@ -138,7 +136,7 @@ public class SimpleMessageRecipient implements IMessageRecipient {
// If this recipient doesn't have a reply recipient, initiate by setting the first
// message sender to this recipient's replyRecipient.
long timeout = ess.getSettings().getLastMessageReplyRecipientTimeout() * 1000;
- if (getReplyRecipient() == null || !getReplyRecipient().isReachable()
+ if (getReplyRecipient() == null || !getReplyRecipient().isReachable()
|| System.currentTimeMillis() - this.lastMessageMs > timeout) {
setReplyRecipient(sender);
}
diff --git a/Essentials/src/com/earth2me/essentials/metrics/Metrics.java b/Essentials/src/com/earth2me/essentials/metrics/Metrics.java
index c4373b2ab..00f640e9f 100644
--- a/Essentials/src/com/earth2me/essentials/metrics/Metrics.java
+++ b/Essentials/src/com/earth2me/essentials/metrics/Metrics.java
@@ -2,43 +2,43 @@ package com.earth2me.essentials.metrics;
import org.bukkit.Bukkit;
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 org.bukkit.plugin.java.JavaPlugin;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import javax.net.ssl.HttpsURLConnection;
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.IOException;
+import java.io.*;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.UUID;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.concurrent.Callable;
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({"WeakerAccess", "unused"})
public class Metrics {
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 (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
- throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
+ // 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 (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
+ throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
+ }
}
}
@@ -48,17 +48,23 @@ public class Metrics {
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bukkit";
+ // Is bStats enabled on this server?
+ private boolean enabled;
+
// Should failed requests be logged?
private static boolean logFailedRequests;
+ // Should the sent data be logged?
+ private static boolean logSentData;
+
+ // Should the response text be logged?
+ private static boolean logResponseStatusText;
+
// The uuid of the server
private static String serverUUID;
- // Whether bStats is enabled.
- private static boolean enabled;
-
// The plugin
- private final JavaPlugin plugin;
+ private final Plugin plugin;
// A list with all custom charts
private final List charts = new ArrayList<>();
@@ -68,7 +74,7 @@ public class Metrics {
*
* @param plugin The plugin which stats should be submitted.
*/
- public Metrics(JavaPlugin plugin) {
+ public Metrics(Plugin plugin) {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null!");
}
@@ -88,6 +94,10 @@ public class Metrics {
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(
@@ -102,10 +112,13 @@ public class Metrics {
}
// Load the data
+ enabled = config.getBoolean("enabled", true);
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
- if (config.getBoolean("enabled", true)) {
- enabled = true;
+ 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()) {
@@ -121,11 +134,18 @@ public class Metrics {
// We are the first!
startSubmitting();
}
- } else {
- enabled = false;
}
}
+ /**
+ * Checks if bStats is enabled.
+ *
+ * @return Whether bStats is enabled or not.
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
/**
* Adds a custom chart.
*
@@ -152,14 +172,9 @@ public class Metrics {
}
// 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() {
- if (enabled) submitData();
- }
- });
+ Bukkit.getScheduler().runTask(plugin, () -> submitData());
}
- }, 1000*60*5, 1000*60*30);
+ }, 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!
@@ -174,7 +189,7 @@ public class Metrics {
public JSONObject getPluginData() {
JSONObject data = new JSONObject();
- String pluginName = "EssentialsX";
+ String pluginName = plugin.getDescription().getName().replace("Essentials", "EssentialsX");
String pluginVersion = plugin.getDescription().getVersion();
data.put("pluginName", pluginName); // Append the name of the plugin
@@ -200,10 +215,19 @@ public class Metrics {
*/
private JSONObject getServerData() {
// Minecraft specific data
- int playerAmount = Bukkit.getOnlinePlayers().size();
+ 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 = org.bukkit.Bukkit.getVersion();
- bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1);
+ String bukkitVersion = Bukkit.getVersion();
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
@@ -233,8 +257,6 @@ public class Metrics {
* Collects the data and sends it afterwards.
*/
private void submitData() {
- if (!enabled) return;
-
final JSONObject data = getServerData();
JSONArray pluginData = new JSONArray();
@@ -242,13 +264,13 @@ public class Metrics {
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) { }
+
+ for (RegisteredServiceProvider> provider : Bukkit.getServicesManager().getRegistrations(service)) {
+ try {
+ pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider()));
+ } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { }
+ }
+ } catch (NoSuchFieldException ignored) { }
}
data.put("plugins", pluginData);
@@ -259,7 +281,7 @@ public class Metrics {
public void run() {
try {
// Send the data
- sendData(data);
+ sendData(plugin, data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
@@ -273,16 +295,20 @@ public class Metrics {
/**
* 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(JSONObject data) throws Exception {
+ private static void sendData(Plugin plugin, JSONObject data) throws Exception {
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 (logSentData) {
+ plugin.getLogger().info("Sending data to bStats: " + data.toString());
+ }
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
@@ -304,7 +330,18 @@ public class Metrics {
outputStream.flush();
outputStream.close();
- connection.getInputStream().close(); // We don't care about the response - Just send our data :)
+ InputStream inputStream = connection.getInputStream();
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
+
+ StringBuilder builder = new StringBuilder();
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ builder.append(line);
+ }
+ bufferedReader.close();
+ if (logResponseStatusText) {
+ plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString());
+ }
}
/**
@@ -320,57 +357,32 @@ public class Metrics {
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
- gzip.write(str.getBytes("UTF-8"));
+ gzip.write(str.getBytes(StandardCharsets.UTF_8));
gzip.close();
return outputStream.toByteArray();
}
- /**
- * Whether Metrics is disabled.
- *
- * @return Whether bStats is disabled.
- */
- public boolean isOptOut() {
- return !enabled;
- }
-
- /**
- * Temporarily enable bStats.
- * This does not change the global config and will reset on server restart.
- */
- public void enable() {
- enabled = true;
- }
-
- /**
- * Temporarily disable bStats.
- * This does not change the global config and will reset on server restart.
- */
- public void disable() {
- enabled = false;
- }
-
/**
* Represents a custom chart.
*/
public static abstract class CustomChart {
// The id of the chart
- protected final String chartId;
+ final String chartId;
/**
* Class constructor.
*
* @param chartId The id of the chart.
*/
- public CustomChart(String chartId) {
+ CustomChart(String chartId) {
if (chartId == null || chartId.isEmpty()) {
throw new IllegalArgumentException("ChartId cannot be null or empty!");
}
this.chartId = chartId;
}
- protected JSONObject getRequestJsonObject() {
+ private JSONObject getRequestJsonObject() {
JSONObject chart = new JSONObject();
chart.put("chartId", chartId);
try {
@@ -389,35 +401,32 @@ public class Metrics {
return chart;
}
- protected abstract JSONObject getChartData();
+ protected abstract JSONObject getChartData() throws Exception;
}
/**
* Represents a custom simple pie.
*/
- public static abstract class SimplePie extends CustomChart {
+ 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) {
+ public SimplePie(String chartId, Callable callable) {
super(chartId);
+ this.callable = callable;
}
- /**
- * Gets the value of the pie.
- *
- * @return The value of the pie.
- */
- public abstract String getValue();
-
@Override
- protected JSONObject getChartData() {
+ protected JSONObject getChartData() throws Exception {
JSONObject data = new JSONObject();
- String value = getValue();
+ String value = callable.call();
if (value == null || value.isEmpty()) {
// Null = skip the chart
return null;
@@ -430,31 +439,26 @@ public class Metrics {
/**
* Represents a custom advanced pie.
*/
- public static abstract class AdvancedPie extends CustomChart {
+ public static class AdvancedPie extends CustomChart {
+
+ private final Callable