Add a better error reporter.

This commit is contained in:
Kristian S. Stangeland 2012-10-29 04:17:53 +01:00
parent 5e036185b8
commit 2239c1ea32
18 changed files with 484 additions and 163 deletions

View File

@ -4,7 +4,7 @@
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>1.4.4-SNAPSHOT</version>
<version>1.5.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://dev.bukkit.org/server-mods/protocollib/</url>
<developers>

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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

View File

@ -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<String, Object> globalParameters = new HashMap<String, Object>();
/**
* 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<String> 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;
}
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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<GamePhase> isInjectionNecessary = new Predicate<GamePhase>() {
@ -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<PacketListener> 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
));

View File

@ -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<Packet
/**
* Invokes the given packet event for every registered listener.
* @param logger - the logger that will be used to inform about listener exceptions.
* @param reporter - the error reporter that will be used to inform about listener exceptions.
* @param event - the packet event to invoke.
*/
public void invokePacketRecieving(Logger logger, PacketEvent event) {
public void invokePacketRecieving(ErrorReporter reporter, PacketEvent event) {
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketID());
if (list == null)
@ -50,19 +48,17 @@ class SortedPacketListenerList extends AbstractConcurrentListenerMultimap<Packet
element.getListener().onPacketReceiving(event);
} catch (Throwable e) {
// Minecraft doesn't want your Exception.
logger.log(Level.SEVERE,
"Exception occured in onPacketReceiving() for " +
PacketAdapter.getPluginName(element.getListener()), e);
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketReceiving()", e);
}
}
}
/**
* Invokes the given packet event for every registered listener.
* @param logger - the logger that will be used to inform about listener exceptions.
* @param reporter - the error reporter that will be used to inform about listener exceptions.
* @param event - the packet event to invoke.
*/
public void invokePacketSending(Logger logger, PacketEvent event) {
public void invokePacketSending(ErrorReporter reporter, PacketEvent event) {
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketID());
if (list == null)
@ -73,9 +69,7 @@ class SortedPacketListenerList extends AbstractConcurrentListenerMultimap<Packet
element.getListener().onPacketSending(event);
} catch (Throwable e) {
// Minecraft doesn't want your Exception.
logger.log(Level.SEVERE,
"Exception occured in onPacketSending() for " +
PacketAdapter.getPluginName(element.getListener()), e);
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketSending()", e);
}
}
}

View File

@ -21,14 +21,13 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.server.NetLoginHandler;
import net.sf.cglib.proxy.Factory;
import org.bukkit.Server;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectCloner;
@ -55,16 +54,16 @@ class InjectedServerConnection {
private NetLoginInjector netLoginInjector;
private Server server;
private Logger logger;
private ErrorReporter reporter;
private boolean hasAttempted;
private boolean hasSuccess;
private Object minecraftServer = null;
public InjectedServerConnection(Logger logger, Server server, NetLoginInjector netLoginInjector) {
public InjectedServerConnection(ErrorReporter reporter, Server server, NetLoginInjector netLoginInjector) {
this.listFields = new ArrayList<VolatileField>();
this.replacedLists = new ArrayList<ReplacedArrayList<Object>>();
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());
}
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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.");
}
}

View File

@ -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<GamePhase> injectionFilter;
public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, Predicate<GamePhase> injectionFilter,
public PlayerInjectionHandler(ClassLoader classLoader, ErrorReporter reporter, Predicate<GamePhase> 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

View File

@ -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);
}
}