diff --git a/ProtocolLib/dependency-reduced-pom.xml b/ProtocolLib/dependency-reduced-pom.xml index 7019dfad..5fb86ef9 100644 --- a/ProtocolLib/dependency-reduced-pom.xml +++ b/ProtocolLib/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ com.comphenix.protocol ProtocolLib ProtocolLib - 1.4.4-SNAPSHOT + 1.5.0 Provides read/write access to the Minecraft protocol. http://dev.bukkit.org/server-mods/protocollib/ diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/AsynchronousManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/AsynchronousManager.java index a16f5db5..a449b1ca 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/AsynchronousManager.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/AsynchronousManager.java @@ -18,11 +18,11 @@ package com.comphenix.protocol; import java.util.Set; -import java.util.logging.Logger; import org.bukkit.plugin.Plugin; import com.comphenix.protocol.async.AsyncListenerHandler; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketListener; @@ -81,10 +81,10 @@ public interface AsynchronousManager { public abstract PacketStream getPacketStream(); /** - * Retrieve the default error logger. - * @return Default logger. + * Retrieve the default error reporter. + * @return Default reporter. */ - public abstract Logger getLogger(); + public abstract ErrorReporter getErrorReporter(); /** * Remove listeners, close threads and transmit every delayed packet. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java b/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java index b3b5e118..269345f9 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java @@ -4,10 +4,9 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; import com.comphenix.protocol.async.AsyncListenerHandler; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.ListeningWhitelist; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.injector.BukkitUnwrapper; @@ -30,11 +29,11 @@ import com.comphenix.protocol.reflect.instances.PrimitiveGenerator; class CleanupStaticMembers { private ClassLoader loader; - private Logger logger; + private ErrorReporter reporter; - public CleanupStaticMembers(ClassLoader loader, Logger logger) { + public CleanupStaticMembers(ClassLoader loader, ErrorReporter reporter) { this.loader = loader; - this.logger = logger; + this.reporter = reporter; } /** @@ -90,8 +89,8 @@ class CleanupStaticMembers { try { setFinalStatic(field, null); } catch (IllegalAccessException e) { - // Just inform us - logger.warning("Unable to reset field " + field.getName() + ": " + e.getMessage()); + // Just inform the player + reporter.reportWarning(this, "Unable to reset field " + field.getName() + ": " + e.getMessage(), e); } } } @@ -126,7 +125,7 @@ class CleanupStaticMembers { output.add(loader.loadClass(name)); } catch (ClassNotFoundException e) { // Warn the user - logger.log(Level.WARNING, "Unable to unload class " + name, e); + reporter.reportWarning(this, "Unable to unload class " + name, e); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java index 85e00881..c17b84d9 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java @@ -18,7 +18,6 @@ package com.comphenix.protocol; import java.io.IOException; -import java.util.logging.Level; import java.util.logging.Logger; import org.bukkit.Server; @@ -26,6 +25,8 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import com.comphenix.protocol.async.AsyncFilterManager; +import com.comphenix.protocol.error.DetailedErrorReporter; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.ConnectionSide; import com.comphenix.protocol.events.MonitorAdapter; import com.comphenix.protocol.events.PacketEvent; @@ -44,9 +45,12 @@ public class ProtocolLibrary extends JavaPlugin { // There should only be one protocol manager, so we'll make it static private static PacketFilterManager protocolManager; - // Error logger + // Information logger private Logger logger; + // Error reporter + private ErrorReporter reporter; + // Metrics and statistisc private Statistics statistisc; @@ -67,39 +71,55 @@ public class ProtocolLibrary extends JavaPlugin { @Override public void onLoad() { - logger = getLoggerSafely(); - unhookTask = new DelayedSingleTask(this); - protocolManager = new PacketFilterManager(getClassLoader(), getServer(), unhookTask, logger); + // Add global parameters + DetailedErrorReporter reporter = new DetailedErrorReporter(); + + try { + logger = getLoggerSafely(); + unhookTask = new DelayedSingleTask(this); + protocolManager = new PacketFilterManager(getClassLoader(), getServer(), unhookTask, reporter); + reporter.addGlobalParameter("manager", protocolManager); + + } catch (Throwable e) { + reporter.reportDetailed(this, "Cannot load ProtocolLib.", e, protocolManager); + } } @Override public void onEnable() { - Server server = getServer(); - PluginManager manager = server.getPluginManager(); - - // Initialize background compiler - if (backgroundCompiler == null) { - backgroundCompiler = new BackgroundCompiler(getClassLoader()); - BackgroundCompiler.setInstance(backgroundCompiler); - } - - // Notify server managers of incompatible plugins - checkForIncompatibility(manager); - - // Player login and logout events - protocolManager.registerEvents(manager, this); + try { + Server server = getServer(); + PluginManager manager = server.getPluginManager(); + + // Initialize background compiler + if (backgroundCompiler == null) { + backgroundCompiler = new BackgroundCompiler(getClassLoader()); + BackgroundCompiler.setInstance(backgroundCompiler); + } + + // Notify server managers of incompatible plugins + checkForIncompatibility(manager); - // Worker that ensures that async packets are eventually sent - createAsyncTask(server); - //toggleDebugListener(); + // Player login and logout events + protocolManager.registerEvents(manager, this); + + // Worker that ensures that async packets are eventually sent + createAsyncTask(server); + //toggleDebugListener(); + + } catch (Throwable e) { + reporter.reportDetailed(this, "Cannot enable ProtocolLib.", e); + disablePlugin(); + return; + } // Try to enable statistics try { statistisc = new Statistics(this); } catch (IOException e) { - logger.log(Level.SEVERE, "Unable to enable metrics.", e); + reporter.reportDetailed(this, "Unable to enable metrics.", e, statistisc); } catch (Throwable e) { - logger.log(Level.SEVERE, "Metrics cannot be enabled. Incompatible Bukkit version.", e); + reporter.reportDetailed(this, "Metrics cannot be enabled. Incompatible Bukkit version.", e, statistisc); } } @@ -136,6 +156,13 @@ public class ProtocolLibrary extends JavaPlugin { debugListener = !debugListener; } + /** + * Disable the current plugin. + */ + private void disablePlugin() { + getServer().getPluginManager().disablePlugin(this); + } + private void createAsyncTask(Server server) { try { if (asyncPacketTask >= 0) @@ -154,7 +181,7 @@ public class ProtocolLibrary extends JavaPlugin { } catch (Throwable e) { if (asyncPacketTask == -1) { - logger.log(Level.SEVERE, "Unable to create packet timeout task.", e); + reporter.reportDetailed(this, "Unable to create packet timeout task.", e); } } } @@ -166,7 +193,7 @@ public class ProtocolLibrary extends JavaPlugin { for (String plugin : incompatiblePlugins) { if (manager.getPlugin(plugin) != null) { // Check for versions, ect. - logger.severe("Detected incompatible plugin: " + plugin); + reporter.reportWarning(this, "Detected incompatible plugin: " + plugin); } } } @@ -192,7 +219,7 @@ public class ProtocolLibrary extends JavaPlugin { statistisc = null; // Leaky ClassLoader begone! - CleanupStaticMembers cleanup = new CleanupStaticMembers(getClassLoader(), logger); + CleanupStaticMembers cleanup = new CleanupStaticMembers(getClassLoader(), reporter); cleanup.resetAll(); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncFilterManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncFilterManager.java index e0707229..7b5744e7 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncFilterManager.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncFilterManager.java @@ -21,7 +21,6 @@ import java.util.Collection; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Logger; import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitScheduler; @@ -29,6 +28,7 @@ import org.bukkit.scheduler.BukkitScheduler; import com.comphenix.protocol.AsynchronousManager; import com.comphenix.protocol.PacketStream; import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.ListeningWhitelist; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketListener; @@ -49,7 +49,7 @@ public class AsyncFilterManager implements AsynchronousManager { private PacketProcessingQueue clientProcessingQueue; private PacketSendingQueue clientQueue; - private Logger logger; + private ErrorReporter reporter; // The likely main thread private Thread mainThread; @@ -66,7 +66,7 @@ public class AsyncFilterManager implements AsynchronousManager { // Whether or not we're currently cleaning up private volatile boolean cleaningUp; - public AsyncFilterManager(Logger logger, BukkitScheduler scheduler, ProtocolManager manager) { + public AsyncFilterManager(ErrorReporter reporter, BukkitScheduler scheduler, ProtocolManager manager) { // Server packets are synchronized already this.serverQueue = new PacketSendingQueue(false); @@ -80,7 +80,7 @@ public class AsyncFilterManager implements AsynchronousManager { this.scheduler = scheduler; this.manager = manager; - this.logger = logger; + this.reporter = reporter; this.mainThread = Thread.currentThread(); } @@ -267,10 +267,10 @@ public class AsyncFilterManager implements AsynchronousManager { } @Override - public Logger getLogger() { - return logger; + public ErrorReporter getErrorReporter() { + return reporter; } - + @Override public void cleanupAll() { cleaningUp = true; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncListenerHandler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncListenerHandler.java index 9f77400f..96d04db3 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncListenerHandler.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncListenerHandler.java @@ -442,8 +442,7 @@ public class AsyncListenerHandler { } catch (Throwable e) { // Minecraft doesn't want your Exception. - filterManager.getLogger().log(Level.SEVERE, - "Unhandled exception occured in onAsyncPacket() for " + getPluginName(), e); + filterManager.getErrorReporter().reportMinimal(listener.getPlugin(), "onAsyncPacket()", e); } // Now, get the next non-cancelled listener diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/error/DetailedErrorReporter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/error/DetailedErrorReporter.java new file mode 100644 index 00000000..13f0a5eb --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/error/DetailedErrorReporter.java @@ -0,0 +1,268 @@ +package com.comphenix.protocol.error; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +import com.comphenix.protocol.events.PacketAdapter; +import com.google.common.primitives.Primitives; + +/** + * Internal class used to handle exceptions. + * + * @author Kristian + */ +public class DetailedErrorReporter implements ErrorReporter { + + public static final String SECOND_LEVEL_PREFIX = " "; + public static final String DEFAULT_PREFIX = " "; + public static final String DEFAULT_SUPPORT_URL = "http://dev.bukkit.org/server-mods/protocollib/"; + public static final String PLUGIN_NAME = "ProtocolLib"; + + // We don't want to spam the server + public static final int DEFAULT_MAX_ERROR_COUNT = 20; + + protected String prefix; + protected String supportURL; + + protected int errorCount; + protected int maxErrorCount; + protected Logger logger; + + // Whether or not Apache Commons is not present + protected boolean apacheCommonsMissing; + + // Map of global objects + protected Map globalParameters = new HashMap(); + + /** + * Create a default error reporting system. + */ + public DetailedErrorReporter() { + this(DEFAULT_PREFIX, DEFAULT_SUPPORT_URL); + } + + /** + * Create a central error reporting system. + * @param prefix - default line prefix. + * @param supportURL - URL to report the error. + */ + public DetailedErrorReporter(String prefix, String supportURL) { + this(prefix, supportURL, DEFAULT_MAX_ERROR_COUNT, getBukkitLogger()); + } + + // Attempt to get the logger. + private static Logger getBukkitLogger() { + try { + return Bukkit.getLogger(); + } catch (Throwable e) { + return Logger.getLogger("Minecraft"); + } + } + + /** + * Create a central error reporting system. + * @param prefix - default line prefix. + * @param supportURL - URL to report the error. + * @param maxErrorCount - number of errors to print before giving up. + * @param logger - current logger. + */ + public DetailedErrorReporter(String prefix, String supportURL, int maxErrorCount, Logger logger) { + this.prefix = prefix; + this.supportURL = supportURL; + this.maxErrorCount = maxErrorCount; + this.logger = logger; + } + + @Override + public void reportMinimal(Plugin sender, String methodName, Throwable error) { + logger.log(Level.SEVERE, "[" + PLUGIN_NAME + "] Unhandled exception occured in " + methodName + " for " + + PacketAdapter.getPluginName(sender), error); + } + + @Override + public void reportWarning(Object sender, String message) { + logger.log(Level.WARNING, "[" + PLUGIN_NAME + "] [" + getSenderName(sender) + "] " + message); + } + + @Override + public void reportWarning(Object sender, String message, Throwable error) { + logger.log(Level.WARNING, "[" + PLUGIN_NAME + "] [" + getSenderName(sender) + "] " + message, error); + } + + private String getSenderName(Object sender) { + if (sender != null) + return sender.getClass().getSimpleName(); + else + return "NULL"; + } + + @Override + public void reportDetailed(Object sender, String message, Throwable error, Object... parameters) { + + // Do not overtly spam the server! + if (++errorCount > maxErrorCount) { + String maxReached = String.format("Reached maxmimum error count. Cannot pass error %s from %s.", error, sender); + logger.severe(maxReached); + return; + } + + StringWriter text = new StringWriter(); + PrintWriter writer = new PrintWriter(text); + + // Helpful message + writer.println("[ProtocolLib] INTERNAL ERROR: " + message); + writer.println("If this problem hasn't already been reported, please open a ticket"); + writer.println("at " + supportURL + " with the following data:"); + + // Now, let us print important exception information + writer.println(" ===== STACK TRACE ====="); + + if (error != null) + error.printStackTrace(writer); + + // Data dump! + writer.println(" ===== DUMP ====="); + + // Relevant parameters + if (parameters != null && parameters.length > 0) { + writer.println("Parameters:"); + + // We *really* want to get as much information as possible + for (Object param : parameters) { + writer.println(addPrefix(getStringDescription(param), SECOND_LEVEL_PREFIX)); + } + } + + // Global parameters + for (String param : globalParameters()) { + writer.println(SECOND_LEVEL_PREFIX + param + ":"); + writer.println(addPrefix(getStringDescription(getGlobalParameter(param)), + SECOND_LEVEL_PREFIX + SECOND_LEVEL_PREFIX)); + } + + // Now, for the sender itself + writer.println("Sender:"); + writer.println(addPrefix(getStringDescription(sender), SECOND_LEVEL_PREFIX)); + + // Add the server version too + if (Bukkit.getServer() != null) { + writer.println("Server:"); + writer.println(addPrefix(Bukkit.getServer().getVersion(), SECOND_LEVEL_PREFIX)); + } + + // Make sure it is reported + logger.severe(addPrefix(text.toString(), prefix)); + } + + /** + * Adds the given prefix to every line in the text. + * @param text - text to modify. + * @param prefix - prefix added to every line in the text. + * @return The modified text. + */ + protected String addPrefix(String text, String prefix) { + return text.replaceAll("(?m)^", prefix); + } + + protected String getStringDescription(Object value) { + + // We can't only rely on toString. + if (value == null) { + return "[NULL]"; + } if (isSimpleType(value)) { + return value.toString(); + } else { + try { + if (!apacheCommonsMissing) + return (ToStringBuilder.reflectionToString(value, ToStringStyle.MULTI_LINE_STYLE, false, null)); + } catch (Throwable ex) { + // Apache is probably missing + logger.warning("Cannot find Apache Commons. Object introspection disabled."); + apacheCommonsMissing = true; + } + + // Just use toString() + return String.format("%s", value); + } + } + + /** + * Determine if the given object is a wrapper for a primitive/simple type or not. + * @param test - the object to test. + * @return TRUE if this object is simple enough to simply be printed, FALSE othewise. + */ + protected boolean isSimpleType(Object test) { + return test instanceof String || Primitives.isWrapperType(test.getClass()); + } + + public int getErrorCount() { + return errorCount; + } + + public void setErrorCount(int errorCount) { + this.errorCount = errorCount; + } + + public int getMaxErrorCount() { + return maxErrorCount; + } + + public void setMaxErrorCount(int maxErrorCount) { + this.maxErrorCount = maxErrorCount; + } + + /** + * Adds the given global parameter. It will be included in every error report. + * @param key - name of parameter. + * @param value - the global parameter itself. + */ + public void addGlobalParameter(String key, Object value) { + globalParameters.put(key, value); + } + + public Object getGlobalParameter(String key) { + return globalParameters.get(key); + } + + public void clearGlobalParameters() { + globalParameters.clear(); + } + + public Set globalParameters() { + return globalParameters.keySet(); + } + + public String getSupportURL() { + return supportURL; + } + + public void setSupportURL(String supportURL) { + this.supportURL = supportURL; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public Logger getLogger() { + return logger; + } + + public void setLogger(Logger logger) { + this.logger = logger; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/error/ErrorReporter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/error/ErrorReporter.java new file mode 100644 index 00000000..de6e1e36 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/error/ErrorReporter.java @@ -0,0 +1,39 @@ +package com.comphenix.protocol.error; + +import org.bukkit.plugin.Plugin; + +public interface ErrorReporter { + + /** + * Prints a small minimal error report about an exception from another plugin. + * @param sender - the other plugin. + * @param methodName - name of the caller method. + * @param error - the exception itself. + */ + public abstract void reportMinimal(Plugin sender, String methodName, Throwable error); + + /** + * Prints a warning message from the current plugin. + * @param sender - the object containing the caller method. + * @param message - error message. + */ + public abstract void reportWarning(Object sender, String message); + + /** + * Prints a warning message from the current plugin. + * @param sender - the object containing the caller method. + * @param message - error message. + * @param error - the exception that was thrown. + */ + public abstract void reportWarning(Object sender, String message, Throwable error); + + /** + * Prints a detailed error report about an unhandled exception. + * @param sender - the object containing the caller method. + * @param message - an error message to include. + * @param error - the exception that was thrown in the caller method. + * @param parameters - parameters from the caller method. + */ + public abstract void reportDetailed(Object sender, String message, Throwable error, Object... parameters); + +} \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketAdapter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketAdapter.java index fbe14775..056fe089 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketAdapter.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketAdapter.java @@ -168,12 +168,20 @@ public abstract class PacketAdapter implements PacketListener { /** * Retrieves the name of the plugin that has been associated with the listener. + * @param listener - the listener. * @return Name of the associated plugin. */ public static String getPluginName(PacketListener listener) { - - Plugin plugin = listener.getPlugin(); - + return getPluginName(listener.getPlugin()); + } + + /** + * Retrieves the name of the given plugin. + * @param plugin - the plugin. + * @return Name of the given plugin. + */ + public static String getPluginName(Plugin plugin) { + // Try to get the plugin name try { if (plugin == null) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java index afbb5fcd..63473dd2 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java @@ -25,8 +25,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.Nullable; @@ -51,6 +49,8 @@ import com.comphenix.protocol.AsynchronousManager; import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.async.AsyncFilterManager; import com.comphenix.protocol.async.AsyncMarker; +import com.comphenix.protocol.error.DetailedErrorReporter; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.*; import com.comphenix.protocol.injector.player.PlayerInjectionHandler; import com.comphenix.protocol.reflect.FieldAccessException; @@ -118,8 +118,8 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok // The default class loader private ClassLoader classLoader; - // Error logger - private Logger logger; + // Error repoter + private ErrorReporter reporter; // The current server private Server server; @@ -142,9 +142,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok * Only create instances of this class if protocol lib is disabled. * @param unhookTask */ - public PacketFilterManager(ClassLoader classLoader, Server server, DelayedSingleTask unhookTask, Logger logger) { - if (logger == null) - throw new IllegalArgumentException("logger cannot be NULL."); + public PacketFilterManager(ClassLoader classLoader, Server server, DelayedSingleTask unhookTask, DetailedErrorReporter reporter) { + if (reporter == null) + throw new IllegalArgumentException("reporter cannot be NULL."); if (classLoader == null) throw new IllegalArgumentException("classLoader cannot be NULL."); @@ -155,7 +155,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok this.unhookTask = unhookTask; this.server = server; this.classLoader = classLoader; - this.logger = logger; + this.reporter = reporter; // Used to determine if injection is needed Predicate isInjectionNecessary = new Predicate() { @@ -174,20 +174,20 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok try { // Initialize injection mangers - this.playerInjection = new PlayerInjectionHandler(classLoader, logger, isInjectionNecessary, this, server); + this.playerInjection = new PlayerInjectionHandler(classLoader, reporter, isInjectionNecessary, this, server); this.packetInjector = new PacketInjector(classLoader, this, playerInjection); - this.asyncFilterManager = new AsyncFilterManager(logger, server.getScheduler(), this); + this.asyncFilterManager = new AsyncFilterManager(reporter, server.getScheduler(), this); // Attempt to load the list of server and client packets try { this.serverPackets = MinecraftRegistry.getServerPackets(); this.clientPackets = MinecraftRegistry.getClientPackets(); } catch (FieldAccessException e) { - logger.log(Level.WARNING, "Cannot load server and client packet list.", e); + reporter.reportWarning(this, "Cannot load server and client packet list.", e); } } catch (IllegalAccessException e) { - logger.log(Level.SEVERE, "Unable to initialize packet injector.", e); + reporter.reportWarning(this, "Unable to initialize packet injector.", e); } } @@ -215,10 +215,6 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok playerInjection.checkListener(packetListeners); } - public Logger getLogger() { - return logger; - } - @Override public ImmutableSet getPacketListeners() { return ImmutableSet.copyOf(packetListeners); @@ -398,9 +394,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok // Process synchronous events if (sending) - packetListeners.invokePacketSending(logger, event); + packetListeners.invokePacketSending(reporter, event); else - packetListeners.invokePacketRecieving(logger, event); + packetListeners.invokePacketRecieving(reporter, event); // To cancel asynchronous processing, use the async marker if (!event.isCancelled() && !hasAsyncCancelled(event.getAsyncMarker())) { @@ -438,7 +434,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok if (serverPackets != null && serverPackets.contains(packetID)) playerInjection.addPacketHandler(packetID); else - logger.warning(String.format( + reporter.reportWarning(this, String.format( "[%s] Unsupported server packet ID in current Minecraft version: %s", PacketAdapter.getPluginName(listener), packetID )); @@ -449,7 +445,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok if (clientPackets != null && clientPackets.contains(packetID)) packetInjector.addPacketHandler(packetID); else - logger.warning(String.format( + reporter.reportWarning(this, String.format( "[%s] Unsupported client packet ID in current Minecraft version: %s", PacketAdapter.getPluginName(listener), packetID )); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java index 94662c9b..5a1ef288 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java @@ -18,11 +18,9 @@ package com.comphenix.protocol.injector; import java.util.Collection; -import java.util.logging.Level; -import java.util.logging.Logger; import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap; -import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketListener; @@ -35,10 +33,10 @@ class SortedPacketListenerList extends AbstractConcurrentListenerMultimap> list = getListener(event.getPacketID()); if (list == null) @@ -50,19 +48,17 @@ class SortedPacketListenerList extends AbstractConcurrentListenerMultimap> list = getListener(event.getPacketID()); if (list == null) @@ -73,9 +69,7 @@ class SortedPacketListenerList extends AbstractConcurrentListenerMultimap(); this.replacedLists = new ArrayList>(); - this.logger = logger; + this.reporter = reporter; this.server = server; this.netLoginInjector = netLoginInjector; } @@ -83,7 +82,7 @@ class InjectedServerConnection { try { minecraftServer = FieldUtils.readField(minecraftServerField, server, true); } catch (IllegalAccessException e1) { - logger.log(Level.WARNING, "Cannot extract minecraft server from Bukkit."); + reporter.reportWarning(this, "Cannot extract minecraft server from Bukkit."); return; } @@ -95,15 +94,13 @@ class InjectedServerConnection { injectServerConnection(); } catch (IllegalArgumentException e) { - // DEBUG - logger.log(Level.WARNING, "Reverting to old 1.2.5 server connection injection.", e); - + // Minecraft 1.2.5 or lower injectListenerThread(); } catch (Exception e) { // Oh damn - inform the player - logger.log(Level.SEVERE, "Cannot inject into server connection. Bad things will happen.", e); + reporter.reportDetailed(this, "Cannot inject into server connection. Bad things will happen.", e); } } @@ -115,7 +112,7 @@ class InjectedServerConnection { listenerThreadField = FuzzyReflection.fromObject(minecraftServer). getFieldByType(".*NetworkListenThread"); } catch (RuntimeException e) { - logger.log(Level.SEVERE, "Cannot find listener thread in MinecraftServer.", e); + reporter.reportDetailed(this, "Cannot find listener thread in MinecraftServer.", e, minecraftServer); return; } @@ -125,7 +122,7 @@ class InjectedServerConnection { try { listenerThread = listenerThreadField.get(minecraftServer); } catch (Exception e) { - logger.log(Level.WARNING, "Unable to read the listener thread.", e); + reporter.reportWarning(this, "Unable to read the listener thread.", e); return; } @@ -142,7 +139,7 @@ class InjectedServerConnection { try { serverConnection = serverConnectionMethod.invoke(minecraftServer); } catch (Exception ex) { - logger.log(Level.WARNING, "Unable to retrieve server connection", ex); + reporter.reportDetailed(this, "Unable to retrieve server connection", ex, minecraftServer); return; } @@ -154,7 +151,7 @@ class InjectedServerConnection { // Verify the field count if (matches.size() != 1) - logger.log(Level.WARNING, "Unexpected number of threads in " + serverConnection.getClass().getName()); + reporter.reportWarning(this, "Unexpected number of threads in " + serverConnection.getClass().getName()); else dedicatedThreadField = matches.get(0); } @@ -164,7 +161,7 @@ class InjectedServerConnection { if (dedicatedThreadField != null) injectEveryListField(FieldUtils.readField(dedicatedThreadField, serverConnection, true), 1); } catch (IllegalAccessException e) { - logger.log(Level.WARNING, "Unable to retrieve net handler thread.", e); + reporter.reportWarning(this, "Unable to retrieve net handler thread.", e); } injectIntoList(serverConnection, listField); @@ -186,7 +183,7 @@ class InjectedServerConnection { // Warn about unexpected errors if (lists.size() < minimum) { - logger.log(Level.WARNING, "Unable to inject " + minimum + " lists in " + container.getClass().getName()); + reporter.reportWarning(this, "Unable to inject " + minimum + " lists in " + container.getClass().getName()); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetLoginInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetLoginInjector.java index 0bc8981a..b5fc924b 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetLoginInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetLoginInjector.java @@ -4,12 +4,11 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.logging.Level; -import java.util.logging.Logger; import org.bukkit.Server; import org.bukkit.entity.Player; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.injector.GamePhase; import com.comphenix.protocol.injector.player.TemporaryPlayerFactory.InjectContainer; import com.google.common.collect.Maps; @@ -27,16 +26,16 @@ class NetLoginInjector { private PlayerInjectionHandler injectionHandler; private Server server; - // The current logger - private Logger logger; + // The current error rerporter + private ErrorReporter reporter; private ReadWriteLock injectionLock = new ReentrantReadWriteLock(); // Used to create fake players private TemporaryPlayerFactory tempPlayerFactory = new TemporaryPlayerFactory(); - public NetLoginInjector(Logger logger, PlayerInjectionHandler injectionHandler, Server server) { - this.logger = logger; + public NetLoginInjector(ErrorReporter reporter, PlayerInjectionHandler injectionHandler, Server server) { + this.reporter = reporter; this.injectionHandler = injectionHandler; this.server = server; } @@ -71,7 +70,7 @@ class NetLoginInjector { } catch (Throwable e) { // Minecraft can't handle this, so we'll deal with it here - logger.log(Level.WARNING, "Unable to hook NetLoginHandler.", e); + reporter.reportDetailed(this, "Unable to hook NetLoginHandler.", e, inserting); return inserting; } finally { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java index 19104e74..2f414f2c 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java @@ -24,11 +24,11 @@ import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; import org.bukkit.entity.Player; import com.comphenix.protocol.Packets; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.ListeningWhitelist; import com.comphenix.protocol.events.PacketListener; import com.comphenix.protocol.injector.GamePhase; @@ -73,10 +73,10 @@ class NetworkFieldInjector extends PlayerInjector { // Used to construct proxy objects private ClassLoader classLoader; - public NetworkFieldInjector(ClassLoader classLoader, Logger logger, Player player, + public NetworkFieldInjector(ClassLoader classLoader, ErrorReporter reporter, Player player, ListenerInvoker manager, IntegerSet sendingFilters) throws IllegalAccessException { - super(logger, player, manager); + super(reporter, player, manager); this.classLoader = classLoader; this.sendingFilters = sendingFilters; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java index 4f98d3cd..6635744d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java @@ -28,11 +28,11 @@ import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; -import java.util.logging.Logger; import org.bukkit.entity.Player; import com.comphenix.protocol.Packets; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.ListeningWhitelist; import com.comphenix.protocol.events.PacketListener; import com.comphenix.protocol.injector.GamePhase; @@ -54,9 +54,9 @@ class NetworkObjectInjector extends PlayerInjector { // Shared callback filter - avoid creating a new class every time private static CallbackFilter callbackFilter; - public NetworkObjectInjector(ClassLoader classLoader, Logger logger, Player player, + public NetworkObjectInjector(ClassLoader classLoader, ErrorReporter reporter, Player player, ListenerInvoker invoker, IntegerSet sendingFilters) throws IllegalAccessException { - super(logger, player, invoker); + super(reporter, player, invoker); this.sendingFilters = sendingFilters; this.classLoader = classLoader; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java index 9ad93166..70ed7a3f 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java @@ -20,8 +20,6 @@ package com.comphenix.protocol.injector.player; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.logging.Level; -import java.util.logging.Logger; import net.minecraft.server.Packet; import net.sf.cglib.proxy.Callback; @@ -34,6 +32,7 @@ import net.sf.cglib.proxy.NoOp; import org.bukkit.entity.Player; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.PacketListener; import com.comphenix.protocol.injector.GamePhase; import com.comphenix.protocol.injector.ListenerInvoker; @@ -68,11 +67,11 @@ public class NetworkServerInjector extends PlayerInjector { private boolean hasDisconnected; public NetworkServerInjector( - ClassLoader classLoader, Logger logger, Player player, + ClassLoader classLoader, ErrorReporter reporter, Player player, ListenerInvoker invoker, IntegerSet sendingFilters, InjectedServerConnection serverInjection) throws IllegalAccessException { - super(logger, player, invoker); + super(reporter, player, invoker); this.classLoader = classLoader; this.sendingFilters = sendingFilters; this.serverInjection = serverInjection; @@ -295,9 +294,9 @@ public class NetworkServerInjector extends PlayerInjector { FieldUtils.writeField(disconnectField, handler, value); } catch (IllegalArgumentException e) { - logger.log(Level.WARNING, "Unable to find disconnect field. Is ProtocolLib up to date?"); + reporter.reportDetailed(this, "Unable to find disconnect field. Is ProtocolLib up to date?", e, handler); } catch (IllegalAccessException e) { - logger.log(Level.WARNING, "Unable to update disconnected field. Player quit event may be sent twice."); + reporter.reportWarning(this, "Unable to update disconnected field. Player quit event may be sent twice."); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java index afc432b9..215fc3c7 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java @@ -24,14 +24,13 @@ import java.net.Socket; import java.net.SocketAddress; import java.util.Map; import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; import net.minecraft.server.Packet; import org.bukkit.Server; import org.bukkit.entity.Player; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketListener; @@ -72,8 +71,8 @@ public class PlayerInjectionHandler { private volatile PlayerInjectHooks loginPlayerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT; private volatile PlayerInjectHooks playingPlayerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT; - // Error logger - private Logger logger; + // Error reporter + private ErrorReporter reporter; // Whether or not we're closing private boolean hasClosed; @@ -90,15 +89,15 @@ public class PlayerInjectionHandler { // Used to filter injection attempts private Predicate injectionFilter; - public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, Predicate injectionFilter, + public PlayerInjectionHandler(ClassLoader classLoader, ErrorReporter reporter, Predicate injectionFilter, ListenerInvoker invoker, Server server) { this.classLoader = classLoader; - this.logger = logger; + this.reporter = reporter; this.invoker = invoker; this.injectionFilter = injectionFilter; - this.netLoginInjector = new NetLoginInjector(logger, this, server); - this.serverInjection = new InjectedServerConnection(logger, server, netLoginInjector); + this.netLoginInjector = new NetLoginInjector(reporter, this, server); + this.serverInjection = new InjectedServerConnection(reporter, server, netLoginInjector); serverInjection.injectList(); } @@ -173,11 +172,11 @@ public class PlayerInjectionHandler { // Construct the correct player hook switch (hook) { case NETWORK_HANDLER_FIELDS: - return new NetworkFieldInjector(classLoader, logger, player, invoker, sendingFilters); + return new NetworkFieldInjector(classLoader, reporter, player, invoker, sendingFilters); case NETWORK_MANAGER_OBJECT: - return new NetworkObjectInjector(classLoader, logger, player, invoker, sendingFilters); + return new NetworkObjectInjector(classLoader, reporter, player, invoker, sendingFilters); case NETWORK_SERVER_OBJECT: - return new NetworkServerInjector(classLoader, logger, player, invoker, sendingFilters, serverInjection); + return new NetworkServerInjector(classLoader, reporter, player, invoker, sendingFilters, serverInjection); default: throw new IllegalArgumentException("Cannot construct a player injector."); } @@ -198,7 +197,7 @@ public class PlayerInjectionHandler { if (injector != null) { return injector.getPlayer(); } else { - logger.warning("Unable to find stream: " + inputStream); + reporter.reportWarning(this, "Unable to find stream: " + inputStream); return null; } @@ -310,7 +309,8 @@ public class PlayerInjectionHandler { } catch (Exception e) { // Mark this injection attempt as a failure - logger.log(Level.SEVERE, "Player hook " + tempHook.toString() + " failed.", e); + reporter.reportDetailed(this, "Player hook " + tempHook.toString() + " failed.", + e, player, injectionPoint, phase); hookFailed = true; } @@ -318,7 +318,7 @@ public class PlayerInjectionHandler { tempHook = PlayerInjectHooks.values()[tempHook.ordinal() - 1]; if (hookFailed) - logger.log(Level.INFO, "Switching to " + tempHook.toString() + " instead."); + reporter.reportWarning(this, "Switching to " + tempHook.toString() + " instead."); // Check for UTTER FAILURE if (tempHook == PlayerInjectHooks.NONE) { @@ -353,8 +353,8 @@ public class PlayerInjectionHandler { try { if (injector != null) injector.cleanupAll(); - } catch (Exception e2) { - logger.log(Level.WARNING, "Cleaing up after player hook failed.", e2); + } catch (Exception ex) { + reporter.reportDetailed(this, "Cleaing up after player hook failed.", ex, injector); } } @@ -415,7 +415,7 @@ public class PlayerInjectionHandler { } catch (IllegalAccessException e) { // Let the user know - logger.log(Level.WARNING, "Unable to fully revert old injector. May cause conflicts.", e); + reporter.reportWarning(this, "Unable to fully revert old injector. May cause conflicts.", e); } } @@ -470,7 +470,7 @@ public class PlayerInjectionHandler { if (injector != null) injector.sendServerPacket(packet.getHandle(), filters); else - logger.log(Level.WARNING, String.format( + reporter.reportWarning(this, String.format( "Unable to send packet %s (%s): Player %s has logged out.", packet.getID(), packet, reciever.getName() )); @@ -491,7 +491,7 @@ public class PlayerInjectionHandler { if (injector != null) injector.processPacket(mcPacket); else - logger.log(Level.WARNING, String.format( + reporter.reportWarning(this, String.format( "Unable to receieve packet %s. Player %s has logged out.", mcPacket, player.getName() )); @@ -537,7 +537,7 @@ public class PlayerInjectionHandler { try { checkListener(listener); } catch (IllegalStateException e) { - logger.log(Level.WARNING, "Unsupported listener.", e); + reporter.reportWarning(this, "Unsupported listener.", e); } } } @@ -565,14 +565,6 @@ public class PlayerInjectionHandler { return sendingFilters.toSet(); } - /** - * Retrieve the current logger. - * @return Error logger. - */ - public Logger getLogger() { - return logger; - } - public void close() { // Guard diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java index a4c2d605..31b2eeda 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java @@ -24,8 +24,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.Socket; import java.net.SocketAddress; -import java.util.logging.Level; -import java.util.logging.Logger; import net.minecraft.server.EntityPlayer; import net.minecraft.server.NetLoginHandler; @@ -36,6 +34,7 @@ import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.entity.Player; import com.comphenix.protocol.Packets; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketListener; @@ -100,7 +99,7 @@ abstract class PlayerInjector { protected DataInputStream cachedInput; // Handle errors - protected Logger logger; + protected ErrorReporter reporter; // Scheduled action on the next packet event protected Runnable scheduledAction; @@ -112,8 +111,8 @@ abstract class PlayerInjector { boolean updateOnLogin; Player updatedPlayer; - public PlayerInjector(Logger logger, Player player, ListenerInvoker invoker) throws IllegalAccessException { - this.logger = logger; + public PlayerInjector(ErrorReporter reporter, Player player, ListenerInvoker invoker) throws IllegalAccessException { + this.reporter = reporter; this.player = player; this.invoker = invoker; } @@ -303,19 +302,24 @@ abstract class PlayerInjector { disconnect.invoke(handler, message); return; } catch (IllegalArgumentException e) { - logger.log(Level.WARNING, "Invalid argument passed to disconnect method: " + message, e); + reporter.reportDetailed(this, "Invalid argument passed to disconnect method: " + message, e, handler); } catch (IllegalAccessException e) { - logger.log(Level.SEVERE, "Unable to access disconnect method.", e); + reporter.reportWarning(this, "Unable to access disconnect method.", e); } } - + // Fuck it try { - getSocket().close(); - } catch (IOException e) { - logger.log(Level.SEVERE, "Unable to close socket.", e); + Socket socket = getSocket(); + + try { + socket.close(); + } catch (IOException e) { + reporter.reportDetailed(this, "Unable to close socket.", e, socket); + } + } catch (IllegalAccessException e) { - logger.log(Level.SEVERE, "Insufficient permissions. Cannot close socket.", e); + reporter.reportWarning(this, "Insufficient permissions. Cannot close socket.", e); } } @@ -332,7 +336,7 @@ abstract class PlayerInjector { return null; hasProxyType = true; - logger.log(Level.WARNING, "Detected server handler proxy type by another plugin. Conflict may occur!"); + reporter.reportWarning(this, "Detected server handler proxy type by another plugin. Conflict may occur!"); // No? Is it a Proxy type? try { @@ -347,7 +351,7 @@ abstract class PlayerInjector { } } catch (IllegalAccessException e) { - logger.warning("Unable to load server handler from proxy type."); + reporter.reportWarning(this, "Unable to load server handler from proxy type."); } // Nope, just go with it @@ -511,7 +515,7 @@ abstract class PlayerInjector { try { updatedPlayer = getEntityPlayer(getNetHandler()).getBukkitEntity(); } catch (IllegalAccessException e) { - logger.log(Level.WARNING, "Cannot update player in PlayerEvent.", e); + reporter.reportDetailed(this, "Cannot update player in PlayerEvent.", e, packet); } }