Compare commits
No files matched your search
@@ -4,7 +4,7 @@
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<name>ProtocolLib</name>
|
||||
<version>1.4.0</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>
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<version>1.5.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.MethodUtils;
|
||||
import com.comphenix.protocol.reflect.ObjectCloner;
|
||||
import com.comphenix.protocol.reflect.PrimitiveUtils;
|
||||
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||
import com.comphenix.protocol.reflect.compiler.StructureCompiler;
|
||||
import com.comphenix.protocol.reflect.instances.CollectionGenerator;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.reflect.instances.PrimitiveGenerator;
|
||||
|
||||
/**
|
||||
* Used to fix ClassLoader leaks that may lead to filling up the permanent generation.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class CleanupStaticMembers {
|
||||
|
||||
private ClassLoader loader;
|
||||
private ErrorReporter reporter;
|
||||
|
||||
public CleanupStaticMembers(ClassLoader loader, ErrorReporter reporter) {
|
||||
this.loader = loader;
|
||||
this.reporter = reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the previous ClassLoader is not leaking.
|
||||
*/
|
||||
public void resetAll() {
|
||||
// This list must always be updated
|
||||
Class<?>[] publicClasses = {
|
||||
AsyncListenerHandler.class, ListeningWhitelist.class, PacketContainer.class,
|
||||
BukkitUnwrapper.class, DefaultInstances.class, CollectionGenerator.class,
|
||||
PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class,
|
||||
BackgroundCompiler.class, StructureCompiler.class,
|
||||
ObjectCloner.class, PrimitiveUtils.class, Packets.Server.class,
|
||||
Packets.Client.class
|
||||
};
|
||||
|
||||
String[] internalClasses = {
|
||||
"com.comphenix.protocol.events.SerializedOfflinePlayer",
|
||||
"com.comphenix.protocol.injector.player.InjectedServerConnection",
|
||||
"com.comphenix.protocol.injector.player.NetworkFieldInjector",
|
||||
"com.comphenix.protocol.injector.player.NetworkObjectInjector",
|
||||
"com.comphenix.protocol.injector.player.NetworkServerInjector",
|
||||
"com.comphenix.protocol.injector.player.PlayerInjector",
|
||||
"com.comphenix.protocol.injector.player.TemporaryPlayerFactory",
|
||||
"com.comphenix.protocol.injector.EntityUtilities",
|
||||
"com.comphenix.protocol.injector.MinecraftRegistry",
|
||||
"com.comphenix.protocol.injector.PacketInjector",
|
||||
"com.comphenix.protocol.injector.ReadPacketModifier",
|
||||
"com.comphenix.protocol.injector.StructureCache",
|
||||
"com.comphenix.protocol.reflect.compiler.BoxingHelper",
|
||||
"com.comphenix.protocol.reflect.compiler.MethodDescriptor"
|
||||
};
|
||||
|
||||
resetClasses(publicClasses);
|
||||
resetClasses(getClasses(loader, internalClasses));
|
||||
}
|
||||
|
||||
private void resetClasses(Class<?>[] classes) {
|
||||
// Reset each class one by one
|
||||
for (Class<?> clazz : classes) {
|
||||
resetClass(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetClass(Class<?> clazz) {
|
||||
for (Field field : clazz.getFields()) {
|
||||
Class<?> type = field.getType();
|
||||
|
||||
// Only check static non-primitive fields. We also skip strings.
|
||||
if (Modifier.isStatic(field.getModifiers()) &&
|
||||
!type.isPrimitive() && !type.equals(String.class)) {
|
||||
|
||||
try {
|
||||
setFinalStatic(field, null);
|
||||
} catch (IllegalAccessException e) {
|
||||
// Just inform the player
|
||||
reporter.reportWarning(this, "Unable to reset field " + field.getName() + ": " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HACK! HAACK!
|
||||
private static void setFinalStatic(Field field, Object newValue) throws IllegalAccessException {
|
||||
int modifier = field.getModifiers();
|
||||
boolean isFinal = Modifier.isFinal(modifier);
|
||||
|
||||
Field modifiersField = isFinal ? FieldUtils.getField(Field.class, "modifiers", true) : null;
|
||||
|
||||
// We have to remove the final field first
|
||||
if (isFinal) {
|
||||
FieldUtils.writeField(modifiersField, field, modifier & ~Modifier.FINAL, true);
|
||||
}
|
||||
|
||||
// Now we can safely modify the field
|
||||
FieldUtils.writeStaticField(field, newValue, true);
|
||||
|
||||
// Revert modifier
|
||||
if (isFinal) {
|
||||
FieldUtils.writeField(modifiersField, field, modifier, true);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?>[] getClasses(ClassLoader loader, String[] names) {
|
||||
List<Class<?>> output = new ArrayList<Class<?>>();
|
||||
|
||||
for (String name : names) {
|
||||
try {
|
||||
output.add(loader.loadClass(name));
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Warn the user
|
||||
reporter.reportWarning(this, "Unable to unload class " + name, e);
|
||||
}
|
||||
}
|
||||
|
||||
return output.toArray(new Class<?>[0]);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +137,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
Object handle = event.getPacket().getHandle();
|
||||
|
||||
System.out.println(String.format(
|
||||
logger.info(String.format(
|
||||
"RECEIVING %s@%s from %s.",
|
||||
handle.getClass().getSimpleName(), handle.hashCode(), event.getPlayer().getName()
|
||||
));
|
||||
@@ -126,7 +146,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
Object handle = event.getPacket().getHandle();
|
||||
|
||||
System.out.println(String.format(
|
||||
logger.info(String.format(
|
||||
"SENDING %s@%s from %s.",
|
||||
handle.getClass().getSimpleName(), handle.hashCode(), event.getPlayer().getName()
|
||||
));
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,6 +217,10 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
protocolManager.close();
|
||||
protocolManager = null;
|
||||
statistisc = null;
|
||||
|
||||
// Leaky ClassLoader begone!
|
||||
CleanupStaticMembers cleanup = new CleanupStaticMembers(getClassLoader(), reporter);
|
||||
cleanup.resetAll();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.Set;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@@ -54,7 +53,7 @@ public class AsyncListenerHandler {
|
||||
private static final AtomicInteger nextID = new AtomicInteger();
|
||||
|
||||
// Default queue capacity
|
||||
private static int DEFAULT_CAPACITY = 1024;
|
||||
private static final int DEFAULT_CAPACITY = 1024;
|
||||
|
||||
// Cancel the async handler
|
||||
private volatile boolean cancelled;
|
||||
@@ -113,10 +112,6 @@ public class AsyncListenerHandler {
|
||||
PacketListener getNullPacketListener() {
|
||||
return nullPacketListener;
|
||||
}
|
||||
|
||||
private String getPluginName() {
|
||||
return PacketAdapter.getPluginName(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the plugin associated with this async listener.
|
||||
@@ -442,8 +437,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
|
||||
|
||||
@@ -205,7 +205,7 @@ class PacketSendingQueue {
|
||||
|
||||
} catch (PlayerLoggedOutException e) {
|
||||
System.out.println(String.format(
|
||||
"Warning: Dropped packet index %s of ID %s",
|
||||
"[ProtocolLib] Warning: Dropped packet index %s of ID %s",
|
||||
marker.getOriginalSendingIndex(), event.getPacketID()
|
||||
));
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -303,7 +303,6 @@ public class PacketContainer implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Entity doesn't exist.");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,4 +46,25 @@ public interface ListenerInvoker {
|
||||
* @return The packet ID.
|
||||
*/
|
||||
public abstract int getPacketID(Packet packet);
|
||||
|
||||
/**
|
||||
* Associate a given class with the given packet ID. Internal method.
|
||||
* @param clazz - class to associate.
|
||||
* @param packetID - the packet ID.
|
||||
*/
|
||||
public abstract void unregisterPacketClass(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Remove a given class from the packet registry. Internal method.
|
||||
* @param clazz - class to remove.
|
||||
*/
|
||||
public abstract void registerPacketClass(Class<?> clazz, int packetID);
|
||||
|
||||
/**
|
||||
* Retrieves the correct packet class from a given packet ID.
|
||||
* @param packetID - the packet ID.
|
||||
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
||||
* @return The associated class.
|
||||
*/
|
||||
public abstract Class<?> getPacketClassFromID(int packetID, boolean forceVanilla);
|
||||
}
|
||||
@@ -157,7 +157,7 @@ class MinecraftRegistry {
|
||||
/**
|
||||
* Retrieves the correct packet class from a given packet ID.
|
||||
* @param packetID - the packet ID.
|
||||
* @param vanilla - whether or not to look for vanilla classes, not injected classes.
|
||||
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
||||
* @return The associated class.
|
||||
*/
|
||||
public static Class getPacketClassFromID(int packetID, boolean forceVanilla) {
|
||||
@@ -172,7 +172,9 @@ class MinecraftRegistry {
|
||||
// Will most likely not be used
|
||||
for (Map.Entry<Class, Integer> entry : getPacketToID().entrySet()) {
|
||||
if (Objects.equal(entry.getValue(), packetID)) {
|
||||
return entry.getKey();
|
||||
// Attempt to get the vanilla class here too
|
||||
if (!forceVanilla || entry.getKey().getName().startsWith("net.minecraft.server"))
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import net.minecraft.server.Packet;
|
||||
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.PrimitiveUtils;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@@ -40,7 +41,7 @@ public class PacketConstructor {
|
||||
* <p>
|
||||
* Remember to call withPacket().
|
||||
*/
|
||||
public static final PacketConstructor DEFAULT = new PacketConstructor(null);
|
||||
public static PacketConstructor DEFAULT = new PacketConstructor(null);
|
||||
|
||||
// The constructor method that's actually responsible for creating the packet
|
||||
private Constructor<?> constructorMethod;
|
||||
@@ -176,7 +177,17 @@ public class PacketConstructor {
|
||||
// Determine if the types are similar
|
||||
if (params.length == types.length) {
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
if (!params[i].isAssignableFrom(types[i])) {
|
||||
Class<?> inputType = types[i];
|
||||
Class<?> paramType = params[i];
|
||||
|
||||
// The input type is always wrapped
|
||||
if (PrimitiveUtils.isPrimitive(paramType)) {
|
||||
// Wrap it
|
||||
paramType = PrimitiveUtils.wrap(paramType);
|
||||
}
|
||||
|
||||
// Compare assignability
|
||||
if (!paramType.isAssignableFrom(inputType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+74
-37
@@ -23,9 +23,8 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
@@ -50,6 +49,7 @@ 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.ErrorReporter;
|
||||
import com.comphenix.protocol.events.*;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
@@ -117,8 +117,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;
|
||||
@@ -134,13 +134,16 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
private AtomicInteger phaseLoginCount = new AtomicInteger(0);
|
||||
private AtomicInteger phasePlayingCount = new AtomicInteger(0);
|
||||
|
||||
// Whether or not plugins are using the send/receive methods
|
||||
private AtomicBoolean packetCreation = new AtomicBoolean();
|
||||
|
||||
/**
|
||||
* 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, ErrorReporter reporter) {
|
||||
if (reporter == null)
|
||||
throw new IllegalArgumentException("reporter cannot be NULL.");
|
||||
if (classLoader == null)
|
||||
throw new IllegalArgumentException("classLoader cannot be NULL.");
|
||||
|
||||
@@ -151,7 +154,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>() {
|
||||
@@ -170,20 +173,20 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
|
||||
try {
|
||||
// Initialize injection mangers
|
||||
this.playerInjection = new PlayerInjectionHandler(classLoader, logger, isInjectionNecessary, this, server);
|
||||
this.packetInjector = new PacketInjector(classLoader, this, playerInjection);
|
||||
this.asyncFilterManager = new AsyncFilterManager(logger, server.getScheduler(), this);
|
||||
this.playerInjection = new PlayerInjectionHandler(classLoader, reporter, isInjectionNecessary, this, packetListeners, server);
|
||||
this.packetInjector = new PacketInjector(classLoader, this, playerInjection, reporter);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,15 +209,8 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
*/
|
||||
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
||||
playerInjection.setPlayerHook(playerHook);
|
||||
|
||||
// Make sure the current listeners are compatible
|
||||
playerInjection.checkListener(packetListeners);
|
||||
}
|
||||
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<PacketListener> getPacketListeners() {
|
||||
return ImmutableSet.copyOf(packetListeners);
|
||||
@@ -394,9 +390,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())) {
|
||||
@@ -434,7 +430,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
|
||||
));
|
||||
@@ -445,7 +441,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
|
||||
));
|
||||
@@ -481,6 +477,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
throw new IllegalArgumentException("reciever cannot be NULL.");
|
||||
if (packet == null)
|
||||
throw new IllegalArgumentException("packet cannot be NULL.");
|
||||
// We may have to enable player injection indefinitely after this
|
||||
if (packetCreation.compareAndSet(false, true))
|
||||
incrementPhases(GamePhase.PLAYING);
|
||||
|
||||
playerInjection.sendServerPacket(reciever, packet, filters);
|
||||
}
|
||||
@@ -492,11 +491,13 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters) throws IllegalAccessException, InvocationTargetException {
|
||||
|
||||
if (sender == null)
|
||||
throw new IllegalArgumentException("sender cannot be NULL.");
|
||||
if (packet == null)
|
||||
throw new IllegalArgumentException("packet cannot be NULL.");
|
||||
// And here too
|
||||
if (packetCreation.compareAndSet(false, true))
|
||||
incrementPhases(GamePhase.PLAYING);
|
||||
|
||||
Packet mcPacket = packet.getHandle();
|
||||
|
||||
@@ -585,26 +586,43 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
manager.registerEvents(new Listener() {
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||
public void onPrePlayerJoin(PlayerJoinEvent event) {
|
||||
// Let's clean up the other injection first.
|
||||
playerInjection.uninjectPlayer(event.getPlayer().getAddress());
|
||||
try {
|
||||
// Let's clean up the other injection first.
|
||||
playerInjection.uninjectPlayer(event.getPlayer().getAddress());
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(PacketFilterManager.this, "Unable to uninject net handler for player.", e, event);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
// This call will be ignored if no listeners are registered
|
||||
playerInjection.injectPlayer(event.getPlayer());
|
||||
try {
|
||||
// This call will be ignored if no listeners are registered
|
||||
playerInjection.injectPlayer(event.getPlayer());
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(PacketFilterManager.this, "Unable to inject player.", e, event);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
playerInjection.uninjectPlayer(event.getPlayer());
|
||||
try {
|
||||
playerInjection.handleDisconnect(event.getPlayer());
|
||||
playerInjection.uninjectPlayer(event.getPlayer());
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(PacketFilterManager.this, "Unable to uninject logged off player.", e, event);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPluginDisabled(PluginDisableEvent event) {
|
||||
// Clean up in case the plugin forgets
|
||||
if (event.getPlugin() != plugin) {
|
||||
removePacketListeners(event.getPlugin());
|
||||
try {
|
||||
// Clean up in case the plugin forgets
|
||||
if (event.getPlugin() != plugin) {
|
||||
removePacketListeners(event.getPlugin());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(PacketFilterManager.this, "Unable handle disabled plugin.", e, event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,6 +658,21 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
return MinecraftRegistry.getPacketToID().get(packet.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerPacketClass(Class<?> clazz, int packetID) {
|
||||
MinecraftRegistry.getPacketToID().put(clazz, packetID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterPacketClass(Class<?> clazz) {
|
||||
MinecraftRegistry.getPacketToID().remove(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPacketClassFromID(int packetID, boolean forceVanilla) {
|
||||
return MinecraftRegistry.getPacketClassFromID(packetID, forceVanilla);
|
||||
}
|
||||
|
||||
// Yes, this is crazy.
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private void registerOld(PluginManager manager, Plugin plugin) {
|
||||
@@ -680,10 +713,14 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
Object event = args[0];
|
||||
|
||||
// Check for the correct event
|
||||
if (event instanceof PlayerJoinEvent)
|
||||
playerInjection.injectPlayer(((PlayerJoinEvent) event).getPlayer());
|
||||
else if (event instanceof PlayerQuitEvent)
|
||||
playerInjection.uninjectPlayer(((PlayerQuitEvent) event).getPlayer());
|
||||
if (event instanceof PlayerJoinEvent) {
|
||||
Player player = ((PlayerJoinEvent) event).getPlayer();
|
||||
playerInjection.injectPlayer(player);
|
||||
} else if (event instanceof PlayerQuitEvent) {
|
||||
Player player = ((PlayerQuitEvent) event).getPlayer();
|
||||
playerInjection.handleDisconnect(player);
|
||||
playerInjection.uninjectPlayer(player);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
@@ -51,6 +52,9 @@ class PacketInjector {
|
||||
// The packet filter manager
|
||||
private ListenerInvoker manager;
|
||||
|
||||
// Error reporter
|
||||
private ErrorReporter reporter;
|
||||
|
||||
// Allows us to determine the sender
|
||||
private PlayerInjectionHandler playerInjection;
|
||||
|
||||
@@ -61,11 +65,12 @@ class PacketInjector {
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public PacketInjector(ClassLoader classLoader, ListenerInvoker manager,
|
||||
PlayerInjectionHandler playerInjection) throws IllegalAccessException {
|
||||
PlayerInjectionHandler playerInjection, ErrorReporter reporter) throws IllegalAccessException {
|
||||
|
||||
this.classLoader = classLoader;
|
||||
this.manager = manager;
|
||||
this.playerInjection = playerInjection;
|
||||
this.reporter = reporter;
|
||||
this.readModifier = new ConcurrentHashMap<Integer, ReadPacketModifier>();
|
||||
initialize();
|
||||
}
|
||||
@@ -129,12 +134,11 @@ class PacketInjector {
|
||||
// Subclass the specific packet class
|
||||
ex.setSuperclass(old);
|
||||
ex.setCallbackType(ReadPacketModifier.class);
|
||||
ex.setUseCache(false);
|
||||
ex.setClassLoader(classLoader);
|
||||
Class proxy = ex.createClass();
|
||||
|
||||
// Create the proxy handler
|
||||
ReadPacketModifier modifier = new ReadPacketModifier(packetID, this);
|
||||
ReadPacketModifier modifier = new ReadPacketModifier(packetID, this, reporter);
|
||||
readModifier.put(packetID, modifier);
|
||||
|
||||
// Add a static reference
|
||||
@@ -142,10 +146,10 @@ class PacketInjector {
|
||||
|
||||
try {
|
||||
// Override values
|
||||
putMethod.invoke(intHashMap, packetID, proxy);
|
||||
previous.put(packetID, old);
|
||||
registry.put(proxy, packetID);
|
||||
overwritten.put(packetID, proxy);
|
||||
putMethod.invoke(intHashMap, packetID, proxy);
|
||||
return true;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
@@ -20,9 +20,12 @@ package com.comphenix.protocol.injector;
|
||||
import java.io.DataInputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
@@ -35,16 +38,23 @@ class ReadPacketModifier implements MethodInterceptor {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static Class[] parameters = { DataInputStream.class };
|
||||
|
||||
// A cancel marker
|
||||
private static final Object CANCEL_MARKER = new Object();
|
||||
|
||||
// Common for all packets of the same type
|
||||
private PacketInjector packetInjector;
|
||||
private int packetID;
|
||||
|
||||
// Whether or not a packet has been cancelled
|
||||
private static WeakHashMap<Object, Object> override = new WeakHashMap<Object, Object>();
|
||||
// Report errors
|
||||
private ErrorReporter reporter;
|
||||
|
||||
public ReadPacketModifier(int packetID, PacketInjector packetInjector) {
|
||||
// Whether or not a packet has been cancelled
|
||||
private static Map<Object, Object> override = Collections.synchronizedMap(new WeakHashMap<Object, Object>());
|
||||
|
||||
public ReadPacketModifier(int packetID, PacketInjector packetInjector, ErrorReporter reporter) {
|
||||
this.packetID = packetID;
|
||||
this.packetInjector = packetInjector;
|
||||
this.reporter = reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,11 +85,12 @@ class ReadPacketModifier implements MethodInterceptor {
|
||||
return proxy.invokeSuper(thisObj, args);
|
||||
}
|
||||
|
||||
if (override.containsKey(thisObj)) {
|
||||
Object overridenObject = override.get(thisObj);
|
||||
|
||||
// Atomic retrieval
|
||||
Object overridenObject = override.get(thisObj);
|
||||
|
||||
if (overridenObject != null) {
|
||||
// This packet has been cancelled
|
||||
if (overridenObject == null) {
|
||||
if (overridenObject == CANCEL_MARKER) {
|
||||
// So, cancel all void methods
|
||||
if (method.getReturnType().equals(Void.TYPE))
|
||||
return null;
|
||||
@@ -96,27 +107,32 @@ class ReadPacketModifier implements MethodInterceptor {
|
||||
if (returnValue == null &&
|
||||
Arrays.equals(method.getParameterTypes(), parameters)) {
|
||||
|
||||
// We need this in order to get the correct player
|
||||
DataInputStream input = (DataInputStream) args[0];
|
||||
|
||||
// Let the people know
|
||||
PacketContainer container = new PacketContainer(packetID, (Packet) thisObj);
|
||||
PacketEvent event = packetInjector.packetRecieved(container, input);
|
||||
|
||||
// Handle override
|
||||
if (event != null) {
|
||||
Packet result = event.getPacket().getHandle();
|
||||
try {
|
||||
// We need this in order to get the correct player
|
||||
DataInputStream input = (DataInputStream) args[0];
|
||||
|
||||
// Let the people know
|
||||
PacketContainer container = new PacketContainer(packetID, (Packet) thisObj);
|
||||
PacketEvent event = packetInjector.packetRecieved(container, input);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
override.put(thisObj, null);
|
||||
} else if (!objectEquals(thisObj, result)) {
|
||||
override.put(thisObj, result);
|
||||
}
|
||||
|
||||
// Update DataInputStream next time
|
||||
if (!event.isCancelled() && packetID == Packets.Server.KEY_RESPONSE) {
|
||||
packetInjector.scheduleDataInputRefresh(event.getPlayer());
|
||||
// Handle override
|
||||
if (event != null) {
|
||||
Packet result = event.getPacket().getHandle();
|
||||
|
||||
if (event.isCancelled()) {
|
||||
override.put(thisObj, CANCEL_MARKER);
|
||||
} else if (!objectEquals(thisObj, result)) {
|
||||
override.put(thisObj, result);
|
||||
}
|
||||
|
||||
// Update DataInputStream next time
|
||||
if (!event.isCancelled() && packetID == Packets.Server.KEY_RESPONSE) {
|
||||
packetInjector.scheduleDataInputRefresh(event.getPlayer());
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// Minecraft cannot handle this error
|
||||
reporter.reportDetailed(this, "Cannot handle clienet packet.", e, args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-13
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
-16
@@ -22,6 +22,7 @@ import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
@@ -73,7 +74,7 @@ class InjectedArrayList extends ArrayList<Packet> {
|
||||
// We'll use the FakePacket marker instead of preventing the filters
|
||||
injector.sendServerPacket(createNegativePacket(packet), true);
|
||||
}
|
||||
|
||||
|
||||
// Collection.add contract
|
||||
return true;
|
||||
|
||||
@@ -90,8 +91,10 @@ class InjectedArrayList extends ArrayList<Packet> {
|
||||
* @return The inverted packet.
|
||||
*/
|
||||
Packet createNegativePacket(Packet source) {
|
||||
Enhancer ex = new Enhancer();
|
||||
Class<?> type = source.getClass();
|
||||
ListenerInvoker invoker = injector.getInvoker();
|
||||
|
||||
int packetID = invoker.getPacketID(source);
|
||||
Class<?> type = invoker.getPacketClassFromID(packetID, true);
|
||||
|
||||
// We want to subtract the byte amount that were added to the running
|
||||
// total of outstanding packets. Otherwise, cancelling too many packets
|
||||
@@ -111,22 +114,38 @@ class InjectedArrayList extends ArrayList<Packet> {
|
||||
// }
|
||||
// ect.
|
||||
// }
|
||||
Enhancer ex = new Enhancer();
|
||||
ex.setSuperclass(type);
|
||||
ex.setInterfaces(new Class[] { FakePacket.class } );
|
||||
ex.setUseCache(true);
|
||||
ex.setClassLoader(classLoader);
|
||||
ex.setSuperclass(type);
|
||||
ex.setCallback(new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
if (method.getReturnType().equals(int.class) && args.length == 0) {
|
||||
Integer result = (Integer) proxy.invokeSuper(obj, args);
|
||||
return -result;
|
||||
} else {
|
||||
return proxy.invokeSuper(obj, args);
|
||||
}
|
||||
}
|
||||
});
|
||||
ex.setCallbackType(InvertedIntegerCallback.class);
|
||||
|
||||
Class<?> proxyClass = ex.createClass();
|
||||
|
||||
// Temporarily associate the fake packet class
|
||||
invoker.registerPacketClass(proxyClass, packetID);
|
||||
|
||||
Packet fake = (Packet) Enhancer.create(proxyClass, new InvertedIntegerCallback());
|
||||
|
||||
return (Packet) ex.create();
|
||||
// Remove this association
|
||||
invoker.unregisterPacketClass(proxyClass);
|
||||
return fake;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverts the integer result of every integer method.
|
||||
* @author Kristian
|
||||
*/
|
||||
private class InvertedIntegerCallback implements MethodInterceptor {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
if (method.getReturnType().equals(int.class) && args.length == 0) {
|
||||
Integer result = (Integer) proxy.invokeSuper(obj, args);
|
||||
return -result;
|
||||
} else {
|
||||
return proxy.invokeSuper(obj, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-18
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +217,12 @@ class InjectedServerConnection {
|
||||
// Is this a normal Minecraft object?
|
||||
if (!(inserting instanceof Factory)) {
|
||||
// If so, copy the content of the old element to the new
|
||||
ObjectCloner.copyTo(inserting, replacement, inserting.getClass());
|
||||
try {
|
||||
ObjectCloner.copyTo(inserting, replacement, inserting.getClass());
|
||||
} catch (Throwable e) {
|
||||
reporter.reportDetailed(InjectedServerConnection.this, "Cannot copy old " + inserting +
|
||||
" to new.", e, inserting, replacement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +243,7 @@ class InjectedServerConnection {
|
||||
// Clean up?
|
||||
if (removing instanceof NetLoginHandler) {
|
||||
netLoginInjector.cleanup(removing);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+37
-9
@@ -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;
|
||||
}
|
||||
@@ -63,12 +62,15 @@ class NetLoginInjector {
|
||||
InjectContainer container = (InjectContainer) fakePlayer;
|
||||
container.setInjector(injector);
|
||||
|
||||
// Save the login
|
||||
injectedLogins.putIfAbsent(inserting, injector);
|
||||
|
||||
// NetServerInjector can never work (currently), so we don't need to replace the NetLoginHandler
|
||||
return inserting;
|
||||
|
||||
} 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 {
|
||||
@@ -93,8 +95,34 @@ class NetLoginInjector {
|
||||
PlayerInjector injected = injectedLogins.get(removing);
|
||||
|
||||
if (injected != null) {
|
||||
injected.cleanupAll();
|
||||
injectedLogins.remove(removing);
|
||||
try {
|
||||
PlayerInjector newInjector = null;
|
||||
Player player = injected.getPlayer();
|
||||
|
||||
// Clean up list
|
||||
injectedLogins.remove(removing);
|
||||
|
||||
// No need to clean up twice
|
||||
if (injected.isClean())
|
||||
return;
|
||||
|
||||
// Hack to clean up other references
|
||||
newInjector = injectionHandler.getInjectorByNetworkHandler(injected.getNetworkManager());
|
||||
|
||||
// Update NetworkManager
|
||||
if (newInjector == null) {
|
||||
injectionHandler.uninjectPlayer(player);
|
||||
} else {
|
||||
injectionHandler.uninjectPlayer(player, false);
|
||||
|
||||
if (injected instanceof NetworkObjectInjector)
|
||||
newInjector.setNetworkManager(injected.getNetworkManager(), true);
|
||||
}
|
||||
|
||||
} catch (Throwable e) {
|
||||
// Don't leak this to Minecraft
|
||||
reporter.reportDetailed(this, "Cannot cleanup NetLoginHandler.", e, removing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-8
@@ -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;
|
||||
}
|
||||
@@ -123,11 +123,14 @@ class NetworkFieldInjector extends PlayerInjector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkListener(PacketListener listener) {
|
||||
public UnsupportedListener checkListener(PacketListener listener) {
|
||||
int[] unsupported = { Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK };
|
||||
|
||||
// Unfortunately, we don't support chunk packets
|
||||
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(),
|
||||
Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK)) {
|
||||
throw new IllegalStateException("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.");
|
||||
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(), unsupported)) {
|
||||
return new UnsupportedListener("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.", unsupported);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +169,7 @@ class NetworkFieldInjector extends PlayerInjector {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void cleanupAll() {
|
||||
protected void cleanHook() {
|
||||
// Clean up
|
||||
for (VolatileField overriden : overridenLists) {
|
||||
List<Packet> minecraftList = (List<Packet>) overriden.getOldValue();
|
||||
@@ -191,6 +194,11 @@ class NetworkFieldInjector extends PlayerInjector {
|
||||
overridenLists.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDisconnect() {
|
||||
// No need to do anything
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInject(GamePhase phase) {
|
||||
// All phases should work
|
||||
|
||||
+33
-17
@@ -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;
|
||||
@@ -50,10 +50,13 @@ class NetworkObjectInjector extends PlayerInjector {
|
||||
|
||||
// Used to construct proxy objects
|
||||
private ClassLoader classLoader;
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -85,11 +88,14 @@ class NetworkObjectInjector extends PlayerInjector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkListener(PacketListener listener) {
|
||||
public UnsupportedListener checkListener(PacketListener listener) {
|
||||
int[] unsupported = { Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK };
|
||||
|
||||
// Unfortunately, we don't support chunk packets
|
||||
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(),
|
||||
Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK)) {
|
||||
throw new IllegalStateException("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.");
|
||||
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(), unsupported)) {
|
||||
return new UnsupportedListener("The NETWORK_OBJECT_INJECTOR hook doesn't support map chunk listeners.", unsupported);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,20 +137,25 @@ class NetworkObjectInjector extends PlayerInjector {
|
||||
}
|
||||
};
|
||||
|
||||
// Share callback filter - that way, we avoid generating a new class every time.
|
||||
if (callbackFilter == null) {
|
||||
callbackFilter = new CallbackFilter() {
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
if (method.equals(queueMethod))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Create our proxy object
|
||||
Enhancer ex = new Enhancer();
|
||||
ex.setClassLoader(classLoader);
|
||||
ex.setSuperclass(networkInterface);
|
||||
ex.setCallbacks(new Callback[] { queueFilter, dispatch });
|
||||
ex.setCallbackFilter(new CallbackFilter() {
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
if (method.equals(queueMethod))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
ex.setCallbackFilter(callbackFilter);
|
||||
|
||||
// Inject it, if we can.
|
||||
networkManagerRef.setValue(ex.create());
|
||||
@@ -152,13 +163,18 @@ class NetworkObjectInjector extends PlayerInjector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupAll() {
|
||||
protected void cleanHook() {
|
||||
// Clean up
|
||||
if (networkManagerRef != null && networkManagerRef.isCurrentSet()) {
|
||||
networkManagerRef.revertValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDisconnect() {
|
||||
// No need to do anything
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInject(GamePhase phase) {
|
||||
// Works for all phases
|
||||
|
||||
+60
-14
@@ -17,9 +17,9 @@
|
||||
|
||||
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.Logger;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
@@ -32,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;
|
||||
@@ -50,6 +51,9 @@ import com.comphenix.protocol.reflect.instances.ExistingGenerator;
|
||||
*/
|
||||
public class NetworkServerInjector extends PlayerInjector {
|
||||
|
||||
private volatile static CallbackFilter callbackFilter;
|
||||
|
||||
private static Field disconnectField;
|
||||
private static Method sendPacketMethod;
|
||||
private InjectedServerConnection serverInjection;
|
||||
|
||||
@@ -59,12 +63,15 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
// Used to create proxy objects
|
||||
private ClassLoader classLoader;
|
||||
|
||||
// Whether or not the player has disconnected
|
||||
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;
|
||||
@@ -164,18 +171,24 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
};
|
||||
Callback noOpCallback = NoOp.INSTANCE;
|
||||
|
||||
// Share callback filter - that way, we avoid generating a new class for
|
||||
// every logged in player.
|
||||
if (callbackFilter == null) {
|
||||
callbackFilter = new CallbackFilter() {
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
if (method.equals(sendPacketMethod))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ex.setClassLoader(classLoader);
|
||||
ex.setSuperclass(serverClass);
|
||||
ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback });
|
||||
ex.setCallbackFilter(new CallbackFilter() {
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
if (method.equals(sendPacketMethod))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
ex.setCallbackFilter(callbackFilter);
|
||||
|
||||
// Find the Minecraft NetServerHandler superclass
|
||||
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
|
||||
@@ -202,6 +215,7 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
if (proxyObject != null) {
|
||||
// This will be done by InjectedServerConnection instead
|
||||
//copyTo(serverHandler, proxyObject);
|
||||
|
||||
serverInjection.replaceServerHandler(serverHandler, proxyObject);
|
||||
serverHandlerRef.setValue(proxyObject);
|
||||
return true;
|
||||
@@ -232,7 +246,7 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupAll() {
|
||||
protected void cleanHook() {
|
||||
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
|
||||
ObjectCloner.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
|
||||
serverHandlerRef.revertValue();
|
||||
@@ -250,14 +264,46 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Prevent the PlayerQuitEvent from being sent twice
|
||||
if (hasDisconnected) {
|
||||
setDisconnect(serverHandlerRef.getValue(), true);
|
||||
}
|
||||
}
|
||||
|
||||
serverInjection.revertServerHandler(serverHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkListener(PacketListener listener) {
|
||||
public void handleDisconnect() {
|
||||
hasDisconnected = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the disconnected field in a NetServerHandler.
|
||||
* @param handler - the NetServerHandler.
|
||||
* @param value - the new value.
|
||||
*/
|
||||
private void setDisconnect(Object handler, boolean value) {
|
||||
// Set it
|
||||
try {
|
||||
// Load the field
|
||||
if (disconnectField == null) {
|
||||
disconnectField = FuzzyReflection.fromObject(handler).getFieldByName("disconnected.*");
|
||||
}
|
||||
FieldUtils.writeField(disconnectField, handler, value);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
reporter.reportDetailed(this, "Unable to find disconnect field. Is ProtocolLib up to date?", e, handler);
|
||||
} catch (IllegalAccessException e) {
|
||||
reporter.reportWarning(this, "Unable to update disconnected field. Player quit event may be sent twice.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnsupportedListener checkListener(PacketListener listener) {
|
||||
// We support everything
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+145
-59
@@ -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;
|
||||
@@ -84,21 +83,25 @@ public class PlayerInjectionHandler {
|
||||
// Enabled packet filters
|
||||
private IntegerSet sendingFilters = new IntegerSet(MAXIMUM_PACKET_ID + 1);
|
||||
|
||||
// List of packet listeners
|
||||
private Set<PacketListener> packetListeners;
|
||||
|
||||
// The class loader we're using
|
||||
private ClassLoader classLoader;
|
||||
|
||||
// Used to filter injection attempts
|
||||
private Predicate<GamePhase> injectionFilter;
|
||||
|
||||
public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, Predicate<GamePhase> injectionFilter,
|
||||
ListenerInvoker invoker, Server server) {
|
||||
public PlayerInjectionHandler(ClassLoader classLoader, ErrorReporter reporter, Predicate<GamePhase> injectionFilter,
|
||||
ListenerInvoker invoker, Set<PacketListener> packetListeners, 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.packetListeners = packetListeners;
|
||||
this.netLoginInjector = new NetLoginInjector(reporter, this, server);
|
||||
this.serverInjection = new InjectedServerConnection(reporter, server, netLoginInjector);
|
||||
serverInjection.injectList();
|
||||
}
|
||||
|
||||
@@ -144,6 +147,9 @@ public class PlayerInjectionHandler {
|
||||
loginPlayerHook = playerHook;
|
||||
if (phase.hasPlaying())
|
||||
playingPlayerHook = playerHook;
|
||||
|
||||
// Make sure the current listeners are compatible
|
||||
checkListener(packetListeners);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,11 +179,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 +204,7 @@ public class PlayerInjectionHandler {
|
||||
if (injector != null) {
|
||||
return injector.getPlayer();
|
||||
} else {
|
||||
System.out.println("Unable to find stream: " + inputStream);
|
||||
reporter.reportWarning(this, "Unable to find stream: " + inputStream);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -281,28 +287,27 @@ public class PlayerInjectionHandler {
|
||||
DataInputStream inputStream = injector.getInputStream(false);
|
||||
|
||||
Socket socket = injector.getSocket();
|
||||
SocketAddress address = socket.getRemoteSocketAddress();
|
||||
SocketAddress address = socket != null ? socket.getRemoteSocketAddress() : null;
|
||||
|
||||
// Make sure the current player is not logged out
|
||||
if (socket.isClosed()) {
|
||||
if (socket != null && socket.isClosed()) {
|
||||
throw new PlayerLoggedOutException();
|
||||
}
|
||||
|
||||
PlayerInjector previous = addressLookup.get(address);
|
||||
// Guard against NPE here too
|
||||
PlayerInjector previous = address != null ? addressLookup.get(address) : null;
|
||||
|
||||
// Close any previously associated hooks before we proceed
|
||||
if (previous != null) {
|
||||
uninjectPlayer(previous.getPlayer());
|
||||
|
||||
// Remove the "hooked" network manager in our instance as well
|
||||
if (previous instanceof NetworkObjectInjector) {
|
||||
injector.setNetworkManager(previous.getNetworkManager(), true);
|
||||
}
|
||||
uninjectPlayer(previous.getPlayer(), false, true);
|
||||
}
|
||||
|
||||
|
||||
injector.injectManager();
|
||||
dataInputLookup.put(inputStream, injector);
|
||||
addressLookup.put(address, injector);
|
||||
|
||||
if (inputStream != null)
|
||||
dataInputLookup.put(inputStream, injector);
|
||||
if (address != null)
|
||||
addressLookup.put(address, injector);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -311,7 +316,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;
|
||||
}
|
||||
|
||||
@@ -319,7 +325,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) {
|
||||
@@ -340,8 +346,10 @@ public class PlayerInjectionHandler {
|
||||
if (permanentHook != getPlayerHook(phase))
|
||||
setPlayerHook(phase, tempHook);
|
||||
|
||||
// Save last injector
|
||||
playerInjection.put(player, injector);
|
||||
// Save injector
|
||||
if (injector != null) {
|
||||
playerInjection.put(player, injector);
|
||||
}
|
||||
}
|
||||
|
||||
return injector;
|
||||
@@ -352,16 +360,50 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke special routines for handling disconnect before a player is uninjected.
|
||||
* @param player - player to process.
|
||||
*/
|
||||
public void handleDisconnect(Player player) {
|
||||
PlayerInjector injector = getInjector(player);
|
||||
|
||||
if (injector != null) {
|
||||
injector.handleDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the given player.
|
||||
* @param player - player to unregister.
|
||||
* @return TRUE if a player has been uninjected, FALSE otherwise.
|
||||
*/
|
||||
public void uninjectPlayer(Player player) {
|
||||
public boolean uninjectPlayer(Player player) {
|
||||
return uninjectPlayer(player, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the given player.
|
||||
* @param player - player to unregister.
|
||||
* @param removeAuxiliary - TRUE to remove auxiliary information, such as input stream and address.
|
||||
* @return TRUE if a player has been uninjected, FALSE otherwise.
|
||||
*/
|
||||
public boolean uninjectPlayer(Player player, boolean removeAuxiliary) {
|
||||
return uninjectPlayer(player, removeAuxiliary, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the given player.
|
||||
* @param player - player to unregister.
|
||||
* @param removeAuxiliary - TRUE to remove auxiliary information, such as input stream and address.
|
||||
* @param prepareNextHook - whether or not we need to fix any lingering hooks.
|
||||
* @return TRUE if a player has been uninjected, FALSE otherwise.
|
||||
*/
|
||||
private boolean uninjectPlayer(Player player, boolean removeAuxiliary, boolean prepareNextHook) {
|
||||
if (!hasClosed && player != null) {
|
||||
|
||||
PlayerInjector injector = playerInjection.remove(player);
|
||||
@@ -371,26 +413,54 @@ public class PlayerInjectionHandler {
|
||||
InetSocketAddress address = player.getAddress();
|
||||
injector.cleanupAll();
|
||||
|
||||
dataInputLookup.remove(input);
|
||||
// Remove the "hooked" network manager in our instance as well
|
||||
if (prepareNextHook && injector instanceof NetworkObjectInjector) {
|
||||
try {
|
||||
PlayerInjector dummyInjector = getHookInstance(player, PlayerInjectHooks.NETWORK_SERVER_OBJECT);
|
||||
dummyInjector.initializePlayer(player);
|
||||
dummyInjector.setNetworkManager(injector.getNetworkManager(), true);
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
// Let the user know
|
||||
reporter.reportWarning(this, "Unable to fully revert old injector. May cause conflicts.", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (address != null)
|
||||
addressLookup.remove(address);
|
||||
// Clean up
|
||||
if (removeAuxiliary) {
|
||||
if (input != null)
|
||||
dataInputLookup.remove(input);
|
||||
if (address != null)
|
||||
addressLookup.remove(address);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a player by the given address.
|
||||
* <p>
|
||||
* If the server handler has been created before we've gotten a chance to unject the player,
|
||||
* the method will try a workaround to remove the injected hook in the NetServerHandler.
|
||||
*
|
||||
* @param address - address of the player to unregister.
|
||||
* @param serverHandler - whether or not the net server handler has already been created.
|
||||
* @return TRUE if a player has been uninjected, FALSE otherwise.
|
||||
*/
|
||||
public void uninjectPlayer(InetSocketAddress address) {
|
||||
public boolean uninjectPlayer(InetSocketAddress address) {
|
||||
if (!hasClosed && address != null) {
|
||||
PlayerInjector injector = addressLookup.get(address);
|
||||
|
||||
// Clean up
|
||||
if (injector != null)
|
||||
uninjectPlayer(injector.getPlayer());
|
||||
uninjectPlayer(injector.getPlayer(), false, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -407,7 +477,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()
|
||||
));
|
||||
@@ -428,7 +498,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()
|
||||
));
|
||||
@@ -443,6 +513,26 @@ public class PlayerInjectionHandler {
|
||||
return playerInjection.get(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a player injector by looking for its NetworkManager.
|
||||
* @param networkManager - current network manager.
|
||||
* @return Related player injector.
|
||||
*/
|
||||
PlayerInjector getInjectorByNetworkHandler(Object networkManager) {
|
||||
// That's not legal
|
||||
if (networkManager == null)
|
||||
return null;
|
||||
|
||||
// O(n) is okay in this instance. This is only a backup solution.
|
||||
for (PlayerInjector injector : playerInjection.values()) {
|
||||
if (injector.getNetworkManager() == networkManager)
|
||||
return injector;
|
||||
}
|
||||
|
||||
// None found
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given listeners are valid.
|
||||
* @param listeners - listeners to check.
|
||||
@@ -451,26 +541,30 @@ public class PlayerInjectionHandler {
|
||||
// Make sure the current listeners are compatible
|
||||
if (lastSuccessfulHook != null) {
|
||||
for (PacketListener listener : listeners) {
|
||||
try {
|
||||
checkListener(listener);
|
||||
} catch (IllegalStateException e) {
|
||||
logger.log(Level.WARNING, "Unsupported listener.", e);
|
||||
}
|
||||
checkListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a listener is valid or not.
|
||||
* <p>
|
||||
* If not, a warning will be printed to the console.
|
||||
* @param listener - listener to check.
|
||||
* @throws IllegalStateException If the given listener's whitelist cannot be fulfilled.
|
||||
*/
|
||||
public void checkListener(PacketListener listener) {
|
||||
try {
|
||||
if (lastSuccessfulHook != null)
|
||||
lastSuccessfulHook.checkListener(listener);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Registering listener " + PacketAdapter.getPluginName(listener) + " failed", e);
|
||||
if (lastSuccessfulHook != null) {
|
||||
UnsupportedListener result = lastSuccessfulHook.checkListener(listener);
|
||||
|
||||
// We won't prevent the listener, as it may still have valid packets
|
||||
if (result != null) {
|
||||
reporter.reportWarning(this, "Cannot fully register listener for " +
|
||||
PacketAdapter.getPluginName(listener) + ": " + result.toString());
|
||||
|
||||
// These are illegal
|
||||
for (int packetID : result.getPackets())
|
||||
removePacketHandler(packetID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,14 +576,6 @@ public class PlayerInjectionHandler {
|
||||
return sendingFilters.toSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current logger.
|
||||
* @return Error logger.
|
||||
*/
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
|
||||
// Guard
|
||||
|
||||
+110
-50
@@ -23,8 +23,7 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.Socket;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import net.minecraft.server.EntityPlayer;
|
||||
import net.minecraft.server.NetLoginHandler;
|
||||
@@ -35,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;
|
||||
@@ -99,17 +99,20 @@ abstract class PlayerInjector {
|
||||
protected DataInputStream cachedInput;
|
||||
|
||||
// Handle errors
|
||||
protected Logger logger;
|
||||
protected ErrorReporter reporter;
|
||||
|
||||
// Scheduled action on the next packet event
|
||||
protected Runnable scheduledAction;
|
||||
|
||||
// Whether or not the injector has been cleaned
|
||||
private boolean clean;
|
||||
|
||||
// Whether or not to update the current player on the first Packet1Login
|
||||
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;
|
||||
}
|
||||
@@ -256,6 +259,21 @@ abstract class PlayerInjector {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the associated address of this player.
|
||||
* @return The associated address.
|
||||
* @throws IllegalAccessException If we're unable to read the socket field.
|
||||
*/
|
||||
public SocketAddress getAddress() throws IllegalAccessException {
|
||||
Socket socket = getSocket();
|
||||
|
||||
// Guard against NULL
|
||||
if (socket != null)
|
||||
return socket.getRemoteSocketAddress();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to disconnect the current client.
|
||||
* @param message - the message to display.
|
||||
@@ -284,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,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 {
|
||||
@@ -328,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
|
||||
@@ -427,7 +450,29 @@ abstract class PlayerInjector {
|
||||
/**
|
||||
* Remove all hooks and modifications.
|
||||
*/
|
||||
public abstract void cleanupAll();
|
||||
public final void cleanupAll() {
|
||||
if (!clean)
|
||||
cleanHook();
|
||||
clean = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up after the player has disconnected.
|
||||
*/
|
||||
public abstract void handleDisconnect();
|
||||
|
||||
/**
|
||||
* Override to add custom cleanup behavior.
|
||||
*/
|
||||
protected abstract void cleanHook();
|
||||
|
||||
/**
|
||||
* Determine whether or not this hook has already been cleaned.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
public boolean isClean() {
|
||||
return clean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this inject method can even be attempted.
|
||||
@@ -444,10 +489,12 @@ abstract class PlayerInjector {
|
||||
/**
|
||||
* Invoked before a new listener is registered.
|
||||
* <p>
|
||||
* The player injector should throw an exception if this listener cannot be properly supplied with packet events.
|
||||
* The player injector should only return a non-null value if some or all of the packet IDs are unsupported.
|
||||
*
|
||||
* @param listener - the listener that is about to be registered.
|
||||
* @return A error message with the unsupported packet IDs, or NULL if this listener is valid.
|
||||
*/
|
||||
public abstract void checkListener(PacketListener listener);
|
||||
public abstract UnsupportedListener checkListener(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Allows a packet to be sent by the listeners.
|
||||
@@ -455,43 +502,48 @@ abstract class PlayerInjector {
|
||||
* @return The given packet, or the packet replaced by the listeners.
|
||||
*/
|
||||
public Packet handlePacketSending(Packet packet) {
|
||||
// Get the packet ID too
|
||||
Integer id = invoker.getPacketID(packet);
|
||||
Player currentPlayer = player;
|
||||
|
||||
// Hack #1: Handle a single scheduled action
|
||||
if (scheduledAction != null) {
|
||||
scheduledAction.run();
|
||||
scheduledAction = null;
|
||||
}
|
||||
// Hack #2
|
||||
if (updateOnLogin) {
|
||||
if (id == Packets.Server.LOGIN) {
|
||||
try {
|
||||
updatedPlayer = getEntityPlayer(getNetHandler()).getBukkitEntity();
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.log(Level.WARNING, "Cannot update player in PlayerEvent.", e);
|
||||
try {
|
||||
// Get the packet ID too
|
||||
Integer id = invoker.getPacketID(packet);
|
||||
Player currentPlayer = player;
|
||||
|
||||
// Hack #1: Handle a single scheduled action
|
||||
if (scheduledAction != null) {
|
||||
scheduledAction.run();
|
||||
scheduledAction = null;
|
||||
}
|
||||
// Hack #2
|
||||
if (updateOnLogin) {
|
||||
if (id == Packets.Server.LOGIN) {
|
||||
try {
|
||||
updatedPlayer = getEntityPlayer(getNetHandler()).getBukkitEntity();
|
||||
} catch (IllegalAccessException e) {
|
||||
reporter.reportDetailed(this, "Cannot update player in PlayerEvent.", e, packet);
|
||||
}
|
||||
}
|
||||
|
||||
// This will only occur in the NetLoginHandler injection
|
||||
if (updatedPlayer != null)
|
||||
currentPlayer = updatedPlayer;
|
||||
}
|
||||
|
||||
// This will only occur in the NetLoginHandler injection
|
||||
if (updatedPlayer != null)
|
||||
currentPlayer = updatedPlayer;
|
||||
}
|
||||
|
||||
// Make sure we're listening
|
||||
if (id != null && hasListener(id)) {
|
||||
// A packet has been sent guys!
|
||||
PacketContainer container = new PacketContainer(id, packet);
|
||||
PacketEvent event = PacketEvent.fromServer(invoker, container, currentPlayer);
|
||||
invoker.invokePacketSending(event);
|
||||
// Make sure we're listening
|
||||
if (id != null && hasListener(id)) {
|
||||
// A packet has been sent guys!
|
||||
PacketContainer container = new PacketContainer(id, packet);
|
||||
PacketEvent event = PacketEvent.fromServer(invoker, container, currentPlayer);
|
||||
invoker.invokePacketSending(event);
|
||||
|
||||
// Cancelling is pretty simple. Just ignore the packet.
|
||||
if (event.isCancelled())
|
||||
return null;
|
||||
|
||||
// Right, remember to replace the packet again
|
||||
return event.getPacket().getHandle();
|
||||
}
|
||||
|
||||
// Cancelling is pretty simple. Just ignore the packet.
|
||||
if (event.isCancelled())
|
||||
return null;
|
||||
|
||||
// Right, remember to replace the packet again
|
||||
return event.getPacket().getHandle();
|
||||
} catch (Throwable e) {
|
||||
reporter.reportDetailed(this, "Cannot handle server packet.", e, packet);
|
||||
}
|
||||
|
||||
return packet;
|
||||
@@ -544,6 +596,14 @@ abstract class PlayerInjector {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object that can invoke the packet events.
|
||||
* @return Packet event invoker.
|
||||
*/
|
||||
public ListenerInvoker getInvoker() {
|
||||
return invoker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the hooked player object OR the more up-to-date player instance.
|
||||
* @return The hooked player, or a more up-to-date instance.
|
||||
|
||||
+30
-2
@@ -68,7 +68,7 @@ class ReplacedArrayList<TKey> extends ArrayList<TKey> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoksed when an element is being removed.
|
||||
* Invoked when an element is being removed.
|
||||
* @param removing - the element being removed.
|
||||
*/
|
||||
protected void onRemoved(TKey removing) {
|
||||
@@ -264,6 +264,15 @@ class ReplacedArrayList<TKey> extends ArrayList<TKey> {
|
||||
addMapping(target, replacement, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the old value, if it exists.
|
||||
* @param target - the key.
|
||||
* @return The value that was replaced, or NULL.
|
||||
*/
|
||||
public TKey getMapping(TKey target) {
|
||||
return replaceMap.get(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a replace rule.
|
||||
* <p>
|
||||
@@ -284,8 +293,9 @@ class ReplacedArrayList<TKey> extends ArrayList<TKey> {
|
||||
/**
|
||||
* Revert the given mapping.
|
||||
* @param target - the instance we replaced.
|
||||
* @return The old mapped value, or NULL if nothing was replaced.
|
||||
*/
|
||||
public synchronized void removeMapping(TKey target) {
|
||||
public synchronized TKey removeMapping(TKey target) {
|
||||
// Make sure the mapping exist
|
||||
if (replaceMap.containsKey(target)) {
|
||||
TKey replacement = replaceMap.get(target);
|
||||
@@ -293,7 +303,25 @@ class ReplacedArrayList<TKey> extends ArrayList<TKey> {
|
||||
|
||||
// Revert existing elements
|
||||
replaceAll(replacement, target);
|
||||
return replacement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap the new replaced value with its old value.
|
||||
* @param target - the instance we replaced.
|
||||
* @param The old mapped value, or NULL if nothing was swapped.
|
||||
*/
|
||||
public synchronized TKey swapMapping(TKey target) {
|
||||
// Make sure the mapping exist
|
||||
TKey replacement = removeMapping(target);
|
||||
|
||||
// Add the reverse
|
||||
if (replacement != null) {
|
||||
replaceMap.put(replacement, target);
|
||||
}
|
||||
return replacement;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+23
-12
@@ -42,6 +42,9 @@ class TemporaryPlayerFactory {
|
||||
// Helpful constructors
|
||||
private final PacketConstructor chatPacket;
|
||||
|
||||
// Prevent too many class creations
|
||||
private static CallbackFilter callbackFilter;
|
||||
|
||||
public TemporaryPlayerFactory() {
|
||||
chatPacket = PacketConstructor.DEFAULT.withPacket(3, new Object[] { "DEMO" });
|
||||
}
|
||||
@@ -76,13 +79,16 @@ class TemporaryPlayerFactory {
|
||||
String methodName = method.getName();
|
||||
PlayerInjector injector = ((InjectContainer) obj).getInjector();
|
||||
|
||||
if (injector == null)
|
||||
throw new IllegalStateException("Unable to find injector.");
|
||||
|
||||
// Use the socket to get the address
|
||||
if (methodName.equalsIgnoreCase("getName"))
|
||||
return "UNKNOWN[" + injector.getSocket().getRemoteSocketAddress() + "]";
|
||||
if (methodName.equalsIgnoreCase("getPlayer"))
|
||||
return injector.getUpdatedPlayer();
|
||||
if (methodName.equalsIgnoreCase("getAddress"))
|
||||
return injector.getSocket().getRemoteSocketAddress();
|
||||
return injector.getAddress();
|
||||
if (methodName.equalsIgnoreCase("getServer"))
|
||||
return server;
|
||||
|
||||
@@ -117,22 +123,27 @@ class TemporaryPlayerFactory {
|
||||
}
|
||||
};
|
||||
|
||||
// Shared callback filter
|
||||
if (callbackFilter == null) {
|
||||
callbackFilter = new CallbackFilter() {
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
// Do not override the object method or the superclass methods
|
||||
if (method.getDeclaringClass().equals(Object.class) ||
|
||||
method.getDeclaringClass().equals(InjectContainer.class))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// CGLib is amazing
|
||||
Enhancer ex = new Enhancer();
|
||||
ex.setSuperclass(InjectContainer.class);
|
||||
ex.setInterfaces(new Class[] { Player.class });
|
||||
ex.setCallbacks(new Callback[] { NoOp.INSTANCE, implementation });
|
||||
ex.setCallbackFilter(new CallbackFilter() {
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
// Do not override the object method or the superclass methods
|
||||
if (method.getDeclaringClass().equals(Object.class) ||
|
||||
method.getDeclaringClass().equals(InjectContainer.class))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
ex.setCallbackFilter(callbackFilter);
|
||||
|
||||
return (Player) ex.create();
|
||||
}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.comphenix.protocol.injector.player;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
/**
|
||||
* Represents an error message from a player injector.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class UnsupportedListener {
|
||||
private String message;
|
||||
private int[] packets;
|
||||
|
||||
/**
|
||||
* Create a new error message.
|
||||
* @param message - the message.
|
||||
* @param packets - unsupported packets.
|
||||
*/
|
||||
public UnsupportedListener(String message, int[] packets) {
|
||||
super();
|
||||
this.message = message;
|
||||
this.packets = packets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the error message.
|
||||
* @return Error message.
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all unsupported packets.
|
||||
* @return Unsupported packets.
|
||||
*/
|
||||
public int[] getPackets() {
|
||||
return packets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s (%s)", message, Joiner.on(", ").join(Arrays.asList(packets)));
|
||||
}
|
||||
}
|
||||
+21
@@ -67,6 +67,16 @@ public abstract class CompiledStructureModifier<TField> extends StructureModifie
|
||||
else
|
||||
return (TField) result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the given field index using reflection.
|
||||
* @param index - index of field.
|
||||
* @return Resulting value.
|
||||
* @throws FieldAccessException The field doesn't exist, or it cannot be accessed under the current security contraints.
|
||||
*/
|
||||
protected Object readReflected(int index) throws FieldAccessException {
|
||||
return super.read(index);
|
||||
}
|
||||
|
||||
protected abstract Object readGenerated(int fieldIndex) throws FieldAccessException;
|
||||
|
||||
@@ -78,6 +88,17 @@ public abstract class CompiledStructureModifier<TField> extends StructureModifie
|
||||
return writeGenerated(index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given field using reflection.
|
||||
* @param index - index of field.
|
||||
* @param value - new value.
|
||||
* @throws FieldAccessException The field doesn't exist, or it cannot be accessed under the current security contraints.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void writeReflected(int index, Object value) throws FieldAccessException {
|
||||
super.write(index, (TField) value);
|
||||
}
|
||||
|
||||
protected abstract StructureModifier<TField> writeGenerated(int index, Object value) throws FieldAccessException;
|
||||
|
||||
@Override
|
||||
|
||||
+2
-3
@@ -335,8 +335,7 @@ public final class StructureCompiler {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, COMPILED_CLASS, "write", "(ILjava/lang/Object;)L" + SUPER_CLASS + ";");
|
||||
mv.visitInsn(Opcodes.POP);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, COMPILED_CLASS, "writeReflected", "(ILjava/lang/Object;)V;");
|
||||
}
|
||||
|
||||
mv.visitJumpInsn(Opcodes.GOTO, returnLabel);
|
||||
@@ -408,7 +407,7 @@ public final class StructureCompiler {
|
||||
// We have to use reflection for private and protected fields.
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, COMPILED_CLASS, "read", "(I)Ljava/lang/Object;");
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, COMPILED_CLASS, "readReflected", "(I)Ljava/lang/Object;");
|
||||
}
|
||||
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
|
||||
-1
@@ -206,7 +206,6 @@ public class DefaultInstances {
|
||||
// Just check if any of them are NULL
|
||||
for (Class<?> type : types) {
|
||||
if (getDefaultInternal(type, providers, recursionLevel) == null) {
|
||||
System.out.println(type.getName() + " is NULL!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: ProtocolLib
|
||||
version: 1.4.0
|
||||
version: 1.5.0
|
||||
description: Provides read/write access to the Minecraft protocol.
|
||||
author: Comphenix
|
||||
website: http://www.comphenix.net/ProtocolLib
|
||||
|
||||
Reference in new issue
Block a user