Merge branch '1.6.2'
Dieser Commit ist enthalten in:
Commit
cd0f8a6fa5
@ -11,12 +11,12 @@
|
|||||||
</arguments>
|
</arguments>
|
||||||
</buildCommand>
|
</buildCommand>
|
||||||
<buildCommand>
|
<buildCommand>
|
||||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
<name>net.sourceforge.metrics.builder</name>
|
||||||
<arguments>
|
<arguments>
|
||||||
</arguments>
|
</arguments>
|
||||||
</buildCommand>
|
</buildCommand>
|
||||||
<buildCommand>
|
<buildCommand>
|
||||||
<name>net.sourceforge.metrics.builder</name>
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
<arguments>
|
<arguments>
|
||||||
</arguments>
|
</arguments>
|
||||||
</buildCommand>
|
</buildCommand>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.comphenix.protocol</groupId>
|
<groupId>com.comphenix.protocol</groupId>
|
||||||
<artifactId>ProtocolLib</artifactId>
|
<artifactId>ProtocolLib</artifactId>
|
||||||
<version>2.4.5</version>
|
<version>2.4.8-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||||
|
|
||||||
@ -29,6 +29,11 @@
|
|||||||
<id>bukkit-rep</id>
|
<id>bukkit-rep</id>
|
||||||
<url>http://repo.bukkit.org/content/groups/public</url>
|
<url>http://repo.bukkit.org/content/groups/public</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>comphenix-releases</id>
|
||||||
|
<name>Comphenix Maven Releases</name>
|
||||||
|
<url>http://repo.comphenix.net/content/repositories/releases/</url>
|
||||||
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@ -200,10 +205,16 @@
|
|||||||
<version>2.2.2</version>
|
<version>2.2.2</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.comphenix.executors</groupId>
|
||||||
|
<artifactId>BukkitExecutors</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bukkit</groupId>
|
<groupId>org.bukkit</groupId>
|
||||||
<artifactId>craftbukkit</artifactId>
|
<artifactId>craftbukkit</artifactId>
|
||||||
<version>1.5.1-R0.2-SNAPSHOT</version>
|
<version>1.6.2-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -95,6 +95,7 @@ public final class Packets {
|
|||||||
public static final int MOB_EFFECT = 41;
|
public static final int MOB_EFFECT = 41;
|
||||||
public static final int REMOVE_MOB_EFFECT = 42;
|
public static final int REMOVE_MOB_EFFECT = 42;
|
||||||
public static final int SET_EXPERIENCE = 43;
|
public static final int SET_EXPERIENCE = 43;
|
||||||
|
public static final int UPDATE_ATTRIBUTES = 44;
|
||||||
public static final int MAP_CHUNK = 51;
|
public static final int MAP_CHUNK = 51;
|
||||||
public static final int MULTI_BLOCK_CHANGE = 52;
|
public static final int MULTI_BLOCK_CHANGE = 52;
|
||||||
public static final int BLOCK_CHANGE = 53;
|
public static final int BLOCK_CHANGE = 53;
|
||||||
@ -199,6 +200,7 @@ public final class Packets {
|
|||||||
public static final int BLOCK_ITEM_SWITCH = 16;
|
public static final int BLOCK_ITEM_SWITCH = 16;
|
||||||
public static final int ARM_ANIMATION = 18;
|
public static final int ARM_ANIMATION = 18;
|
||||||
public static final int ENTITY_ACTION = 19;
|
public static final int ENTITY_ACTION = 19;
|
||||||
|
public static final int PLAYER_INPUT = 27;
|
||||||
public static final int CLOSE_WINDOW = 101;
|
public static final int CLOSE_WINDOW = 101;
|
||||||
public static final int WINDOW_CLICK = 102;
|
public static final int WINDOW_CLICK = 102;
|
||||||
public static final int TRANSACTION = 106;
|
public static final int TRANSACTION = 106;
|
||||||
|
@ -41,6 +41,7 @@ import com.comphenix.protocol.error.ErrorReporter;
|
|||||||
import com.comphenix.protocol.error.Report;
|
import com.comphenix.protocol.error.Report;
|
||||||
import com.comphenix.protocol.error.ReportType;
|
import com.comphenix.protocol.error.ReportType;
|
||||||
import com.comphenix.protocol.injector.DelayedSingleTask;
|
import com.comphenix.protocol.injector.DelayedSingleTask;
|
||||||
|
import com.comphenix.protocol.injector.InternalManager;
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
import com.comphenix.protocol.metrics.Statistics;
|
import com.comphenix.protocol.metrics.Statistics;
|
||||||
@ -85,7 +86,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
/**
|
/**
|
||||||
* The maximum version ProtocolLib has been tested with,
|
* The maximum version ProtocolLib has been tested with,
|
||||||
*/
|
*/
|
||||||
private static final String MAXIMUM_MINECRAFT_VERSION = "1.5.2";
|
private static final String MAXIMUM_MINECRAFT_VERSION = "1.6.1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of milliseconds per second.
|
* The number of milliseconds per second.
|
||||||
@ -95,7 +96,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
private static final String PERMISSION_INFO = "protocol.info";
|
private static final String PERMISSION_INFO = "protocol.info";
|
||||||
|
|
||||||
// There should only be one protocol manager, so we'll make it static
|
// There should only be one protocol manager, so we'll make it static
|
||||||
private static PacketFilterManager protocolManager;
|
private static InternalManager protocolManager;
|
||||||
|
|
||||||
// Error reporter
|
// Error reporter
|
||||||
private static ErrorReporter reporter = new BasicErrorReporter();
|
private static ErrorReporter reporter = new BasicErrorReporter();
|
||||||
@ -172,8 +173,14 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
updater = new Updater(this, logger, "protocollib", getFile(), "protocol.info");
|
updater = new Updater(this, logger, "protocollib", getFile(), "protocol.info");
|
||||||
|
|
||||||
unhookTask = new DelayedSingleTask(this);
|
unhookTask = new DelayedSingleTask(this);
|
||||||
protocolManager = new PacketFilterManager(
|
protocolManager = PacketFilterManager.newBuilder().
|
||||||
getClassLoader(), getServer(), this, version, unhookTask, reporter);
|
classLoader(getClassLoader()).
|
||||||
|
server(getServer()).
|
||||||
|
library(this).
|
||||||
|
minecraftVersion(version).
|
||||||
|
unhookTask(unhookTask).
|
||||||
|
reporter(reporter).
|
||||||
|
build();
|
||||||
|
|
||||||
// Setup error reporter
|
// Setup error reporter
|
||||||
detailedReporter.addGlobalParameter("manager", protocolManager);
|
detailedReporter.addGlobalParameter("manager", protocolManager);
|
||||||
@ -216,7 +223,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Report filterReport(Object sender, Report report, boolean detailed) {
|
protected Report filterReport(Object sender, Report report, boolean detailed) {
|
||||||
String canonicalName = ReportType.getReportName(sender.getClass(), report.getType());
|
String canonicalName = ReportType.getReportName(sender, report.getType());
|
||||||
String reportName = Iterables.getLast(Splitter.on("#").split(canonicalName)).toUpperCase();
|
String reportName = Iterables.getLast(Splitter.on("#").split(canonicalName)).toUpperCase();
|
||||||
|
|
||||||
if (config != null && config.getModificationCount() != lastModCount) {
|
if (config != null && config.getModificationCount() != lastModCount) {
|
||||||
@ -302,9 +309,6 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform logic when the world has loaded
|
|
||||||
protocolManager.postWorldLoaded();
|
|
||||||
|
|
||||||
// Initialize background compiler
|
// Initialize background compiler
|
||||||
if (backgroundCompiler == null && config.isBackgroundCompilerEnabled()) {
|
if (backgroundCompiler == null && config.isBackgroundCompilerEnabled()) {
|
||||||
backgroundCompiler = new BackgroundCompiler(getClassLoader(), reporter);
|
backgroundCompiler = new BackgroundCompiler(getClassLoader(), reporter);
|
||||||
|
@ -69,12 +69,12 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||||||
// Default scheduler
|
// Default scheduler
|
||||||
private final BukkitScheduler scheduler;
|
private final BukkitScheduler scheduler;
|
||||||
|
|
||||||
// Our protocol manager
|
|
||||||
private final ProtocolManager manager;
|
|
||||||
|
|
||||||
// Current packet index
|
// Current packet index
|
||||||
private final AtomicInteger currentSendingIndex = new AtomicInteger();
|
private final AtomicInteger currentSendingIndex = new AtomicInteger();
|
||||||
|
|
||||||
|
// Our protocol manager
|
||||||
|
private ProtocolManager manager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a asynchronous filter manager.
|
* Initialize a asynchronous filter manager.
|
||||||
* <p>
|
* <p>
|
||||||
@ -83,7 +83,7 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||||||
* @param scheduler - task scheduler.
|
* @param scheduler - task scheduler.
|
||||||
* @param manager - protocol manager.
|
* @param manager - protocol manager.
|
||||||
*/
|
*/
|
||||||
public AsyncFilterManager(ErrorReporter reporter, BukkitScheduler scheduler, ProtocolManager manager) {
|
public AsyncFilterManager(ErrorReporter reporter, BukkitScheduler scheduler) {
|
||||||
// Initialize timeout listeners
|
// Initialize timeout listeners
|
||||||
this.serverTimeoutListeners = new SortedPacketListenerList();
|
this.serverTimeoutListeners = new SortedPacketListenerList();
|
||||||
this.clientTimeoutListeners = new SortedPacketListenerList();
|
this.clientTimeoutListeners = new SortedPacketListenerList();
|
||||||
@ -95,12 +95,26 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||||||
this.playerSendingHandler.initializeScheduler();
|
this.playerSendingHandler.initializeScheduler();
|
||||||
|
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.manager = manager;
|
|
||||||
|
|
||||||
this.reporter = reporter;
|
this.reporter = reporter;
|
||||||
this.mainThread = Thread.currentThread();
|
this.mainThread = Thread.currentThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the protocol manager.
|
||||||
|
* @return The protocol manager.
|
||||||
|
*/
|
||||||
|
public ProtocolManager getManager() {
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the associated protocol manager.
|
||||||
|
* @param manager - the new manager.
|
||||||
|
*/
|
||||||
|
public void setManager(ProtocolManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AsyncListenerHandler registerAsyncHandler(PacketListener listener) {
|
public AsyncListenerHandler registerAsyncHandler(PacketListener listener) {
|
||||||
return registerAsyncHandler(listener, true);
|
return registerAsyncHandler(listener, true);
|
||||||
|
@ -59,7 +59,7 @@ public class DelegatedErrorReporter implements ErrorReporter {
|
|||||||
* Invoked before an error report is passed on to the underlying error reporter.
|
* Invoked before an error report is passed on to the underlying error reporter.
|
||||||
* <p>
|
* <p>
|
||||||
* To cancel a report, return NULL.
|
* To cancel a report, return NULL.
|
||||||
* @param sender - the sender component.
|
* @param sender - the sender instance or class.
|
||||||
* @param report - the error report.
|
* @param report - the error report.
|
||||||
* @param detailed - whether or not the report will be displayed in detail.
|
* @param detailed - whether or not the report will be displayed in detail.
|
||||||
* @return The report to pass on, or NULL to cancel it.
|
* @return The report to pass on, or NULL to cancel it.
|
||||||
|
@ -224,7 +224,7 @@ public class DetailedErrorReporter implements ErrorReporter {
|
|||||||
*/
|
*/
|
||||||
private String getSenderName(Object sender) {
|
private String getSenderName(Object sender) {
|
||||||
if (sender != null)
|
if (sender != null)
|
||||||
return sender.getClass().getSimpleName();
|
return ReportType.getSenderClass(sender).getSimpleName();
|
||||||
else
|
else
|
||||||
return "NULL";
|
return "NULL";
|
||||||
}
|
}
|
||||||
@ -345,7 +345,7 @@ public class DetailedErrorReporter implements ErrorReporter {
|
|||||||
// We can't only rely on toString.
|
// We can't only rely on toString.
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return "[NULL]";
|
return "[NULL]";
|
||||||
} if (isSimpleType(value)) {
|
} if (isSimpleType(value) || value instanceof Class<?>) {
|
||||||
return value.toString();
|
return value.toString();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
@ -46,6 +46,39 @@ public class ReportType {
|
|||||||
return errorFormat;
|
return errorFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the class of the given sender.
|
||||||
|
* <p>
|
||||||
|
* If the sender is already a Class, we return it.
|
||||||
|
* @param sender - the sender to look up.
|
||||||
|
* @return The class of the sender.
|
||||||
|
*/
|
||||||
|
public static Class<?> getSenderClass(Object sender) {
|
||||||
|
if (sender == null)
|
||||||
|
throw new IllegalArgumentException("sender cannot be NUll.");
|
||||||
|
else if (sender instanceof Class<?>)
|
||||||
|
return (Class<?>) sender;
|
||||||
|
else
|
||||||
|
return sender.getClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the full canonical name of a given report type.
|
||||||
|
* <p>
|
||||||
|
* Note that the sender may be a class (for static callers), in which
|
||||||
|
* case it will be used directly instead of its getClass() method.
|
||||||
|
* <p>
|
||||||
|
* It is thus not advisable for class classes to report reports.
|
||||||
|
* @param sender - the sender, or its class.
|
||||||
|
* @param type - the report type.
|
||||||
|
* @return The full canonical name.
|
||||||
|
*/
|
||||||
|
public static String getReportName(Object sender, ReportType type) {
|
||||||
|
if (sender == null)
|
||||||
|
throw new IllegalArgumentException("sender cannot be NUll.");
|
||||||
|
return getReportName(getSenderClass(sender), type);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the full canonical name of a given report type.
|
* Retrieve the full canonical name of a given report type.
|
||||||
* <p>
|
* <p>
|
||||||
@ -54,7 +87,7 @@ public class ReportType {
|
|||||||
* @param type - the report instance.
|
* @param type - the report instance.
|
||||||
* @return The full canonical name.
|
* @return The full canonical name.
|
||||||
*/
|
*/
|
||||||
public static String getReportName(Class<?> sender, ReportType type) {
|
private static String getReportName(Class<?> sender, ReportType type) {
|
||||||
if (sender == null)
|
if (sender == null)
|
||||||
throw new IllegalArgumentException("sender cannot be NUll.");
|
throw new IllegalArgumentException("sender cannot be NUll.");
|
||||||
|
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
|
|
||||||
package com.comphenix.protocol.events;
|
package com.comphenix.protocol.events;
|
||||||
|
|
||||||
|
import java.io.DataInput;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutput;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
@ -49,6 +51,7 @@ import com.comphenix.protocol.reflect.cloning.CollectionCloner;
|
|||||||
import com.comphenix.protocol.reflect.cloning.FieldCloner;
|
import com.comphenix.protocol.reflect.cloning.FieldCloner;
|
||||||
import com.comphenix.protocol.reflect.cloning.ImmutableDetector;
|
import com.comphenix.protocol.reflect.cloning.ImmutableDetector;
|
||||||
import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters;
|
import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
import com.comphenix.protocol.utility.StreamSerializer;
|
import com.comphenix.protocol.utility.StreamSerializer;
|
||||||
@ -456,7 +459,7 @@ public class PacketContainer implements Serializable {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Call the write-method
|
// Call the write-method
|
||||||
getMethodLazily(writeMethods, handle.getClass(), "write", DataOutputStream.class).
|
getMethodLazily(writeMethods, handle.getClass(), "write", DataOutput.class).
|
||||||
invoke(handle, new DataOutputStream(output));
|
invoke(handle, new DataOutputStream(output));
|
||||||
|
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@ -483,7 +486,7 @@ public class PacketContainer implements Serializable {
|
|||||||
|
|
||||||
// Call the read method
|
// Call the read method
|
||||||
try {
|
try {
|
||||||
getMethodLazily(readMethods, handle.getClass(), "read", DataInputStream.class).
|
getMethodLazily(readMethods, handle.getClass(), "read", DataInput.class).
|
||||||
invoke(handle, new DataInputStream(input));
|
invoke(handle, new DataInputStream(input));
|
||||||
|
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@ -513,7 +516,12 @@ public class PacketContainer implements Serializable {
|
|||||||
|
|
||||||
// Atomic operation
|
// Atomic operation
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
Method initialized = FuzzyReflection.fromClass(handleClass).getMethodByParameters(methodName, parameterClass);
|
Method initialized = FuzzyReflection.fromClass(handleClass).getMethod(
|
||||||
|
FuzzyMethodContract.newBuilder().
|
||||||
|
parameterCount(1).
|
||||||
|
parameterDerivedOf(parameterClass).
|
||||||
|
returnTypeVoid().
|
||||||
|
build());
|
||||||
method = lookup.putIfAbsent(handleClass, initialized);
|
method = lookup.putIfAbsent(handleClass, initialized);
|
||||||
|
|
||||||
// Use our version if we succeeded
|
// Use our version if we succeeded
|
||||||
|
@ -0,0 +1,389 @@
|
|||||||
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.AsynchronousManager;
|
||||||
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
|
import com.comphenix.protocol.error.Report;
|
||||||
|
import com.comphenix.protocol.error.ReportType;
|
||||||
|
import com.comphenix.protocol.events.ConnectionSide;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A protocol manager that delays all packet listener registrations and unregistrations until
|
||||||
|
* an underlying protocol manager can be constructed.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class DelayedPacketManager implements ProtocolManager, InternalManager {
|
||||||
|
// Registering packet IDs that are not supported
|
||||||
|
public static final ReportType REPORT_CANNOT_SEND_QUEUED_PACKET = new ReportType("Cannot send queued packet %s.");
|
||||||
|
public static final ReportType REPORT_CANNOT_REGISTER_QUEUED_LISTENER = new ReportType("Cannot register queued listener %s.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a packet that will be transmitted later.
|
||||||
|
* @author Kristian
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static class QueuedPacket {
|
||||||
|
private final Player player;
|
||||||
|
private final PacketContainer packet;
|
||||||
|
private final boolean filtered;
|
||||||
|
private final ConnectionSide side;
|
||||||
|
|
||||||
|
public QueuedPacket(Player player, PacketContainer packet, boolean filtered, ConnectionSide side) {
|
||||||
|
this.player = player;
|
||||||
|
this.packet = packet;
|
||||||
|
this.filtered = filtered;
|
||||||
|
this.side = side;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the packet that will be transmitted or receieved.
|
||||||
|
* @return The packet.
|
||||||
|
*/
|
||||||
|
public PacketContainer getPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the player that will send or recieve the packet.
|
||||||
|
* @return The source.
|
||||||
|
*/
|
||||||
|
public Player getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve whether or not the packet will the sent or received.
|
||||||
|
* @return The connection side.
|
||||||
|
*/
|
||||||
|
public ConnectionSide getSide() {
|
||||||
|
return side;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the packet should be intercepted by packet listeners.
|
||||||
|
* @return TRUE if it should, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isFiltered() {
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private volatile InternalManager delegate;
|
||||||
|
|
||||||
|
// Packet listeners that will be registered
|
||||||
|
private final Set<PacketListener> queuedListeners = Sets.newSetFromMap(Maps.<PacketListener, Boolean>newConcurrentMap());
|
||||||
|
private final List<QueuedPacket> queuedPackets = Collections.synchronizedList(Lists.<QueuedPacket>newArrayList());
|
||||||
|
|
||||||
|
private AsynchronousManager asyncManager;
|
||||||
|
private ErrorReporter reporter;
|
||||||
|
|
||||||
|
// The current hook
|
||||||
|
private PlayerInjectHooks hook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||||
|
|
||||||
|
// If we have been closed
|
||||||
|
private boolean closed;
|
||||||
|
|
||||||
|
// Queued registration
|
||||||
|
private PluginManager queuedManager;
|
||||||
|
private Plugin queuedPlugin;
|
||||||
|
|
||||||
|
public DelayedPacketManager(@Nonnull ErrorReporter reporter) {
|
||||||
|
Preconditions.checkNotNull(reporter, "reporter cannot be NULL.");
|
||||||
|
|
||||||
|
this.reporter = reporter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the underlying protocol manager.
|
||||||
|
* @return The underlying manager.
|
||||||
|
*/
|
||||||
|
public InternalManager getDelegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the delegate to the underlying manager.
|
||||||
|
* <p>
|
||||||
|
* This will prompt this packet manager to immediately transmit and
|
||||||
|
* register all queued packets an listeners.
|
||||||
|
* @param delegate - delegate to the new manager.
|
||||||
|
*/
|
||||||
|
protected void setDelegate(InternalManager delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
|
||||||
|
if (delegate != null) {
|
||||||
|
// Update the hook if needed
|
||||||
|
if (!Objects.equal(delegate.getPlayerHook(), hook)) {
|
||||||
|
delegate.setPlayerHook(hook);
|
||||||
|
}
|
||||||
|
// Register events as well
|
||||||
|
if (queuedManager != null && queuedPlugin != null) {
|
||||||
|
delegate.registerEvents(queuedManager, queuedPlugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PacketListener listener : queuedListeners) {
|
||||||
|
try {
|
||||||
|
delegate.addPacketListener(listener);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Inform about this plugin error
|
||||||
|
reporter.reportWarning(this,
|
||||||
|
Report.newBuilder(REPORT_CANNOT_REGISTER_QUEUED_LISTENER).
|
||||||
|
callerParam(delegate).messageParam(listener).error(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (queuedPackets) {
|
||||||
|
for (QueuedPacket packet : queuedPackets) {
|
||||||
|
try {
|
||||||
|
// Attempt to send it now
|
||||||
|
switch (packet.getSide()) {
|
||||||
|
case CLIENT_SIDE:
|
||||||
|
delegate.recieveClientPacket(packet.getPlayer(), packet.getPacket(), packet.isFiltered());
|
||||||
|
break;
|
||||||
|
case SERVER_SIDE:
|
||||||
|
delegate.sendServerPacket(packet.getPlayer(), packet.getPacket(), packet.isFiltered());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Inform about this plugin error
|
||||||
|
reporter.reportWarning(this,
|
||||||
|
Report.newBuilder(REPORT_CANNOT_SEND_QUEUED_PACKET).
|
||||||
|
callerParam(delegate).messageParam(packet).error(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't keep this around anymore
|
||||||
|
queuedListeners.clear();
|
||||||
|
queuedPackets.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
||||||
|
this.hook = playerHook;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlayerInjectHooks getPlayerHook() {
|
||||||
|
return hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendServerPacket(Player reciever, PacketContainer packet) throws InvocationTargetException {
|
||||||
|
sendServerPacket(reciever, packet, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException {
|
||||||
|
if (delegate != null) {
|
||||||
|
delegate.sendServerPacket(reciever, packet, filters);
|
||||||
|
} else {
|
||||||
|
queuedPackets.add(new QueuedPacket(reciever, packet, filters, ConnectionSide.SERVER_SIDE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recieveClientPacket(Player sender, PacketContainer packet) throws IllegalAccessException, InvocationTargetException {
|
||||||
|
recieveClientPacket(sender, packet, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters) throws IllegalAccessException, InvocationTargetException {
|
||||||
|
if (delegate != null) {
|
||||||
|
delegate.recieveClientPacket(sender, packet, filters);
|
||||||
|
} else {
|
||||||
|
queuedPackets.add(new QueuedPacket(sender, packet, filters, ConnectionSide.CLIENT_SIDE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImmutableSet<PacketListener> getPacketListeners() {
|
||||||
|
if (delegate != null)
|
||||||
|
return delegate.getPacketListeners();
|
||||||
|
else
|
||||||
|
return ImmutableSet.copyOf(queuedListeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPacketListener(PacketListener listener) {
|
||||||
|
if (delegate != null)
|
||||||
|
delegate.addPacketListener(listener);
|
||||||
|
else
|
||||||
|
queuedListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removePacketListener(PacketListener listener) {
|
||||||
|
if (delegate != null)
|
||||||
|
delegate.removePacketListener(listener);
|
||||||
|
else
|
||||||
|
queuedListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removePacketListeners(Plugin plugin) {
|
||||||
|
if (delegate != null) {
|
||||||
|
delegate.removePacketListeners(plugin);
|
||||||
|
} else {
|
||||||
|
for (Iterator<PacketListener> it = queuedListeners.iterator(); it.hasNext(); ) {
|
||||||
|
// Remove listeners of the same plugin
|
||||||
|
if (Objects.equal(it.next().getPlugin(), plugin)) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketContainer createPacket(int id) {
|
||||||
|
if (delegate != null)
|
||||||
|
return delegate.createPacket(id);
|
||||||
|
return createPacket(id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketContainer createPacket(int id, boolean forceDefaults) {
|
||||||
|
if (delegate != null) {
|
||||||
|
return delegate.createPacket(id);
|
||||||
|
} else {
|
||||||
|
// Fallback implementation
|
||||||
|
PacketContainer packet = new PacketContainer(id);
|
||||||
|
|
||||||
|
// Use any default values if possible
|
||||||
|
if (forceDefaults) {
|
||||||
|
try {
|
||||||
|
packet.getModifier().writeDefaults();
|
||||||
|
} catch (FieldAccessException e) {
|
||||||
|
throw new RuntimeException("Security exception.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketConstructor createPacketConstructor(int id, Object... arguments) {
|
||||||
|
if (delegate != null)
|
||||||
|
return delegate.createPacketConstructor(id, arguments);
|
||||||
|
else
|
||||||
|
return PacketConstructor.DEFAULT.withPacket(id, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Integer> getSendingFilters() {
|
||||||
|
if (delegate != null) {
|
||||||
|
return delegate.getSendingFilters();
|
||||||
|
} else {
|
||||||
|
// Linear scan is fast enough here
|
||||||
|
Set<Integer> sending = Sets.newHashSet();
|
||||||
|
|
||||||
|
for (PacketListener listener : queuedListeners) {
|
||||||
|
sending.addAll(listener.getSendingWhitelist().getWhitelist());
|
||||||
|
}
|
||||||
|
return sending;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Integer> getReceivingFilters() {
|
||||||
|
if (delegate != null) {
|
||||||
|
return delegate.getReceivingFilters();
|
||||||
|
} else {
|
||||||
|
Set<Integer> recieving = Sets.newHashSet();
|
||||||
|
|
||||||
|
for (PacketListener listener : queuedListeners) {
|
||||||
|
recieving.addAll(listener.getReceivingWhitelist().getWhitelist());
|
||||||
|
}
|
||||||
|
return recieving;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
|
||||||
|
if (delegate != null)
|
||||||
|
delegate.updateEntity(entity, observers);
|
||||||
|
else
|
||||||
|
EntityUtilities.updateEntity(entity, observers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entity getEntityFromID(World container, int id) throws FieldAccessException {
|
||||||
|
if (delegate != null)
|
||||||
|
return delegate.getEntityFromID(container, id);
|
||||||
|
else
|
||||||
|
return EntityUtilities.getEntityFromID(container, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Player> getEntityTrackers(Entity entity) throws FieldAccessException {
|
||||||
|
if (delegate != null)
|
||||||
|
return delegate.getEntityTrackers(entity);
|
||||||
|
else
|
||||||
|
return EntityUtilities.getEntityTrackers(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClosed() {
|
||||||
|
return closed || (delegate != null && delegate.isClosed());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AsynchronousManager getAsynchronousManager() {
|
||||||
|
if (delegate != null)
|
||||||
|
return delegate.getAsynchronousManager();
|
||||||
|
else
|
||||||
|
return asyncManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the asynchronous manager. This must be set.
|
||||||
|
* @param asyncManager - the asynchronous manager.
|
||||||
|
*/
|
||||||
|
public void setAsynchronousManager(AsynchronousManager asyncManager) {
|
||||||
|
this.asyncManager = asyncManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerEvents(PluginManager manager, Plugin plugin) {
|
||||||
|
if (delegate != null) {
|
||||||
|
delegate.registerEvents(manager, plugin);
|
||||||
|
} else {
|
||||||
|
queuedManager = manager;
|
||||||
|
queuedPlugin = plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (delegate != null)
|
||||||
|
delegate.close();
|
||||||
|
closed = true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields access to the internal hook configuration.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public interface InternalManager extends ProtocolManager {
|
||||||
|
/**
|
||||||
|
* Retrieves how the server packets are read.
|
||||||
|
* @return Injection method for reading server packets.
|
||||||
|
*/
|
||||||
|
public PlayerInjectHooks getPlayerHook();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets how the server packets are read.
|
||||||
|
* @param playerHook - the new injection method for reading server packets.
|
||||||
|
*/
|
||||||
|
public void setPlayerHook(PlayerInjectHooks playerHook);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register this protocol manager on Bukkit.
|
||||||
|
* @param manager - Bukkit plugin manager that provides player join/leave events.
|
||||||
|
* @param plugin - the parent plugin.
|
||||||
|
*/
|
||||||
|
public void registerEvents(PluginManager manager, final Plugin plugin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when ProtocolLib is closing.
|
||||||
|
*/
|
||||||
|
public void close();
|
||||||
|
}
|
@ -0,0 +1,255 @@
|
|||||||
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.event.world.WorldInitEvent;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import com.comphenix.executors.BukkitFutures;
|
||||||
|
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||||
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
|
import com.comphenix.protocol.error.Report;
|
||||||
|
import com.comphenix.protocol.error.ReportType;
|
||||||
|
import com.comphenix.protocol.injector.player.InjectedServerConnection;
|
||||||
|
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||||
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
|
||||||
|
public class PacketFilterBuilder {
|
||||||
|
public static final ReportType REPORT_TEMPORARY_EVENT_ERROR = new ReportType("Unable to register or handle temporary event.");
|
||||||
|
|
||||||
|
private ClassLoader classLoader;
|
||||||
|
private Server server;
|
||||||
|
private Plugin library;
|
||||||
|
private MinecraftVersion mcVersion;
|
||||||
|
private DelayedSingleTask unhookTask;
|
||||||
|
private ErrorReporter reporter;
|
||||||
|
|
||||||
|
// Whether or not we need to enable Netty
|
||||||
|
private AsyncFilterManager asyncManager;
|
||||||
|
private boolean nettyEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the current class loader.
|
||||||
|
* @param classLoader - current class loader.
|
||||||
|
* @return This builder, for chaining.
|
||||||
|
*/
|
||||||
|
public PacketFilterBuilder classLoader(@Nonnull ClassLoader classLoader) {
|
||||||
|
if (classLoader == null)
|
||||||
|
throw new IllegalArgumentException("classLoader cannot be NULL.");
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current server.
|
||||||
|
* @param server - current server.
|
||||||
|
* @return This builder, for chaining.
|
||||||
|
*/
|
||||||
|
public PacketFilterBuilder server(@Nonnull Server server) {
|
||||||
|
if (server == null)
|
||||||
|
throw new IllegalArgumentException("server cannot be NULL.");
|
||||||
|
this.server = server;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a reference to the plugin instance of ProtocolLib.
|
||||||
|
* @param library - plugin instance.
|
||||||
|
* @return This builder, for chaining.
|
||||||
|
*/
|
||||||
|
public PacketFilterBuilder library(@Nonnull Plugin library) {
|
||||||
|
if (library == null)
|
||||||
|
throw new IllegalArgumentException("library cannot be NULL.");
|
||||||
|
this.library = library;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current Minecraft version.
|
||||||
|
* @param mcVersion - Minecraft version.
|
||||||
|
* @return This builder, for chaining.
|
||||||
|
*/
|
||||||
|
public PacketFilterBuilder minecraftVersion(@Nonnull MinecraftVersion mcVersion) {
|
||||||
|
if (mcVersion == null)
|
||||||
|
throw new IllegalArgumentException("minecraftVersion cannot be NULL.");
|
||||||
|
this.mcVersion = mcVersion;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the task used to delay unhooking when ProtocolLib is no in use.
|
||||||
|
* @param unhookTask - the unhook task.
|
||||||
|
* @return This builder, for chaining.
|
||||||
|
*/
|
||||||
|
public PacketFilterBuilder unhookTask(@Nonnull DelayedSingleTask unhookTask) {
|
||||||
|
if (unhookTask == null)
|
||||||
|
throw new IllegalArgumentException("unhookTask cannot be NULL.");
|
||||||
|
this.unhookTask = unhookTask;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the error reporter.
|
||||||
|
* @param reporter - new error reporter.
|
||||||
|
* @return This builder, for chaining.
|
||||||
|
*/
|
||||||
|
public PacketFilterBuilder reporter(@Nonnull ErrorReporter reporter) {
|
||||||
|
if (reporter == null)
|
||||||
|
throw new IllegalArgumentException("reporter cannot be NULL.");
|
||||||
|
this.reporter = reporter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if we should prepare to hook Netty in Spigot.
|
||||||
|
* <p>
|
||||||
|
* This is calculated in the {@link #build()} method.
|
||||||
|
* @return TRUE if we should, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isNettyEnabled() {
|
||||||
|
return nettyEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the class loader set in this builder.
|
||||||
|
* @return The class loader.
|
||||||
|
*/
|
||||||
|
public ClassLoader getClassLoader() {
|
||||||
|
return classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the current CraftBukkit server.
|
||||||
|
* @return Current server.
|
||||||
|
*/
|
||||||
|
public Server getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a reference to the current ProtocolLib instance.
|
||||||
|
* @return ProtocolLib.
|
||||||
|
*/
|
||||||
|
public Plugin getLibrary() {
|
||||||
|
return library;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the current Minecraft version.
|
||||||
|
* @return Current version.
|
||||||
|
*/
|
||||||
|
public MinecraftVersion getMinecraftVersion() {
|
||||||
|
return mcVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the task that is used to delay unhooking when ProtocolLib is no in use.
|
||||||
|
* @return The unhook task.
|
||||||
|
*/
|
||||||
|
public DelayedSingleTask getUnhookTask() {
|
||||||
|
return unhookTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the error reporter.
|
||||||
|
* @return Error reporter.
|
||||||
|
*/
|
||||||
|
public ErrorReporter getReporter() {
|
||||||
|
return reporter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the asynchronous manager.
|
||||||
|
* <p>
|
||||||
|
* This is first constructed the {@link #build()} method.
|
||||||
|
* @return The asynchronous manager.
|
||||||
|
*/
|
||||||
|
public AsyncFilterManager getAsyncManager() {
|
||||||
|
return asyncManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new packet filter manager.
|
||||||
|
* @return A new packet filter manager.
|
||||||
|
*/
|
||||||
|
public InternalManager build() {
|
||||||
|
if (reporter == null)
|
||||||
|
throw new IllegalArgumentException("reporter cannot be NULL.");
|
||||||
|
if (classLoader == null)
|
||||||
|
throw new IllegalArgumentException("classLoader cannot be NULL.");
|
||||||
|
|
||||||
|
asyncManager = new AsyncFilterManager(reporter, server.getScheduler());
|
||||||
|
nettyEnabled = false;
|
||||||
|
|
||||||
|
// Spigot
|
||||||
|
if (SpigotPacketInjector.canUseSpigotListener()) {
|
||||||
|
// If the server hasn't loaded yet - wait
|
||||||
|
if (InjectedServerConnection.getServerConnection(reporter, server) == null) {
|
||||||
|
// We need to delay this until we know if Netty is enabled
|
||||||
|
final DelayedPacketManager delayed = new DelayedPacketManager(reporter);
|
||||||
|
|
||||||
|
// They must reference each other
|
||||||
|
delayed.setAsynchronousManager(asyncManager);
|
||||||
|
asyncManager.setManager(delayed);
|
||||||
|
|
||||||
|
Futures.addCallback(BukkitFutures.nextEvent(library, WorldInitEvent.class),
|
||||||
|
new FutureCallback<WorldInitEvent>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(WorldInitEvent event) {
|
||||||
|
// Nevermind
|
||||||
|
if (delayed.isClosed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
registerSpigot(delayed);
|
||||||
|
} catch (Exception e) {
|
||||||
|
onFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable error) {
|
||||||
|
reporter.reportWarning(PacketFilterBuilder.this, Report
|
||||||
|
.newBuilder(REPORT_TEMPORARY_EVENT_ERROR).error(error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
System.out.println("Delaying due to Spigot");
|
||||||
|
|
||||||
|
// Let plugins use this version instead
|
||||||
|
return delayed;
|
||||||
|
} else {
|
||||||
|
nettyEnabled = !MinecraftReflection.isMinecraftObject(
|
||||||
|
InjectedServerConnection.getServerConnection(reporter, server));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise - construct the packet filter manager right away
|
||||||
|
return buildInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerSpigot(DelayedPacketManager delayed) {
|
||||||
|
// Use netty if we have a non-standard ServerConnection class
|
||||||
|
nettyEnabled = !MinecraftReflection.isMinecraftObject(
|
||||||
|
InjectedServerConnection.getServerConnection(reporter, server));
|
||||||
|
|
||||||
|
// Switch to the standard manager
|
||||||
|
delayed.setDelegate(buildInternal());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new packet filter manager without checking for Netty.
|
||||||
|
* @return A new packet filter manager.
|
||||||
|
*/
|
||||||
|
private PacketFilterManager buildInternal() {
|
||||||
|
PacketFilterManager manager = new PacketFilterManager(this);
|
||||||
|
|
||||||
|
// It's a cyclic reference, but it's too late to fix now
|
||||||
|
asyncManager.setManager(manager);
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
}
|
@ -63,12 +63,12 @@ import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
|
|||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
public final class PacketFilterManager implements ProtocolManager, ListenerInvoker {
|
public final class PacketFilterManager implements ProtocolManager, ListenerInvoker, InternalManager {
|
||||||
|
|
||||||
public static final ReportType REPORT_CANNOT_LOAD_PACKET_LIST = new ReportType("Cannot load server and client packet list.");
|
public static final ReportType REPORT_CANNOT_LOAD_PACKET_LIST = new ReportType("Cannot load server and client packet list.");
|
||||||
public static final ReportType REPORT_CANNOT_INITIALIZE_PACKET_INJECTOR = new ReportType("Unable to initialize packet injector");
|
public static final ReportType REPORT_CANNOT_INITIALIZE_PACKET_INJECTOR = new ReportType("Unable to initialize packet injector");
|
||||||
|
|
||||||
@ -85,6 +85,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
public static final ReportType REPORT_CANNOT_INJECT_PLAYER = new ReportType("Unable to inject player.");
|
public static final ReportType REPORT_CANNOT_INJECT_PLAYER = new ReportType("Unable to inject player.");
|
||||||
|
|
||||||
public static final ReportType REPORT_CANNOT_UNREGISTER_PLUGIN = new ReportType("Unable to handle disabled plugin.");
|
public static final ReportType REPORT_CANNOT_UNREGISTER_PLUGIN = new ReportType("Unable to handle disabled plugin.");
|
||||||
|
public static final ReportType REPORT_PLUGIN_VERIFIER_ERROR = new ReportType("Verifier error: %s");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the inject hook type. Different types allow for maximum compatibility.
|
* Sets the inject hook type. Different types allow for maximum compatibility.
|
||||||
@ -172,37 +173,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
/**
|
/**
|
||||||
* Only create instances of this class if protocol lib is disabled.
|
* Only create instances of this class if protocol lib is disabled.
|
||||||
*/
|
*/
|
||||||
public PacketFilterManager(ClassLoader classLoader, Server server, Plugin library, DelayedSingleTask unhookTask, ErrorReporter reporter) {
|
public PacketFilterManager(PacketFilterBuilder builder) {
|
||||||
this(classLoader, server, library, new MinecraftVersion(server), unhookTask, reporter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only create instances of this class if protocol lib is disabled.
|
|
||||||
*/
|
|
||||||
public PacketFilterManager(ClassLoader classLoader, Server server, Plugin library,
|
|
||||||
MinecraftVersion mcVersion, DelayedSingleTask unhookTask, ErrorReporter reporter) {
|
|
||||||
|
|
||||||
if (reporter == null)
|
|
||||||
throw new IllegalArgumentException("reporter cannot be NULL.");
|
|
||||||
if (classLoader == null)
|
|
||||||
throw new IllegalArgumentException("classLoader cannot be NULL.");
|
|
||||||
|
|
||||||
// Just boilerplate
|
|
||||||
final DelayedSingleTask finalUnhookTask = unhookTask;
|
|
||||||
|
|
||||||
// Listener containers
|
|
||||||
this.recievedListeners = new SortedPacketListenerList();
|
|
||||||
this.sendingListeners = new SortedPacketListenerList();
|
|
||||||
|
|
||||||
// References
|
|
||||||
this.unhookTask = unhookTask;
|
|
||||||
this.server = server;
|
|
||||||
this.classLoader = classLoader;
|
|
||||||
this.reporter = reporter;
|
|
||||||
|
|
||||||
// The plugin verifier
|
|
||||||
this.pluginVerifier = new PluginVerifier(library);
|
|
||||||
|
|
||||||
// Used to determine if injection is needed
|
// Used to determine if injection is needed
|
||||||
Predicate<GamePhase> isInjectionNecessary = new Predicate<GamePhase>() {
|
Predicate<GamePhase> isInjectionNecessary = new Predicate<GamePhase>() {
|
||||||
@Override
|
@Override
|
||||||
@ -213,14 +184,26 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
result &= getPhaseLoginCount() > 0;
|
result &= getPhaseLoginCount() > 0;
|
||||||
// Note that we will still hook players if the unhooking has been delayed
|
// Note that we will still hook players if the unhooking has been delayed
|
||||||
if (phase.hasPlaying())
|
if (phase.hasPlaying())
|
||||||
result &= getPhasePlayingCount() > 0 || finalUnhookTask.isRunning();
|
result &= getPhasePlayingCount() > 0 || unhookTask.isRunning();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
// Listener containers
|
||||||
// Spigot
|
this.recievedListeners = new SortedPacketListenerList();
|
||||||
if (SpigotPacketInjector.canUseSpigotListener()) {
|
this.sendingListeners = new SortedPacketListenerList();
|
||||||
|
|
||||||
|
// References
|
||||||
|
this.unhookTask = builder.getUnhookTask();
|
||||||
|
this.server = builder.getServer();
|
||||||
|
this.classLoader = builder.getClassLoader();
|
||||||
|
this.reporter = builder.getReporter();
|
||||||
|
|
||||||
|
// The plugin verifier
|
||||||
|
this.pluginVerifier = new PluginVerifier(builder.getLibrary());
|
||||||
|
|
||||||
|
// Use the correct injection type
|
||||||
|
if (builder.isNettyEnabled()) {
|
||||||
spigotInjector = new SpigotPacketInjector(classLoader, reporter, this, server);
|
spigotInjector = new SpigotPacketInjector(classLoader, reporter, this, server);
|
||||||
this.playerInjection = spigotInjector.getPlayerHandler();
|
this.playerInjection = spigotInjector.getPlayerHandler();
|
||||||
this.packetInjector = spigotInjector.getPacketInjector();
|
this.packetInjector = spigotInjector.getPacketInjector();
|
||||||
@ -234,7 +217,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
classLoader(classLoader).
|
classLoader(classLoader).
|
||||||
packetListeners(packetListeners).
|
packetListeners(packetListeners).
|
||||||
injectionFilter(isInjectionNecessary).
|
injectionFilter(isInjectionNecessary).
|
||||||
version(mcVersion).
|
version(builder.getMinecraftVersion()).
|
||||||
buildHandler();
|
buildHandler();
|
||||||
|
|
||||||
this.packetInjector = PacketInjectorBuilder.newBuilder().
|
this.packetInjector = PacketInjectorBuilder.newBuilder().
|
||||||
@ -244,8 +227,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
playerInjection(playerInjection).
|
playerInjection(playerInjection).
|
||||||
buildInjector();
|
buildInjector();
|
||||||
}
|
}
|
||||||
|
this.asyncFilterManager = builder.getAsyncManager();
|
||||||
this.asyncFilterManager = new AsyncFilterManager(reporter, server.getScheduler(), this);
|
|
||||||
|
|
||||||
// Attempt to load the list of server and client packets
|
// Attempt to load the list of server and client packets
|
||||||
try {
|
try {
|
||||||
@ -254,17 +236,14 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
} catch (FieldAccessException e) {
|
} catch (FieldAccessException e) {
|
||||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_LOAD_PACKET_LIST).error(e));
|
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_LOAD_PACKET_LIST).error(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (FieldAccessException e) {
|
|
||||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_INITIALIZE_PACKET_INJECTOR).error(e));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiate logic that is performed after the world has loaded.
|
* Construct a new packet filter builder.
|
||||||
|
* @return New builder.
|
||||||
*/
|
*/
|
||||||
public void postWorldLoaded() {
|
public static PacketFilterBuilder newBuilder() {
|
||||||
playerInjection.postWorldLoaded();
|
return new PacketFilterBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -276,6 +255,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
* Retrieves how the server packets are read.
|
* Retrieves how the server packets are read.
|
||||||
* @return Injection method for reading server packets.
|
* @return Injection method for reading server packets.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public PlayerInjectHooks getPlayerHook() {
|
public PlayerInjectHooks getPlayerHook() {
|
||||||
return playerInjection.getPlayerHook();
|
return playerInjection.getPlayerHook();
|
||||||
}
|
}
|
||||||
@ -284,6 +264,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
* Sets how the server packets are read.
|
* Sets how the server packets are read.
|
||||||
* @param playerHook - the new injection method for reading server packets.
|
* @param playerHook - the new injection method for reading server packets.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
||||||
playerInjection.setPlayerHook(playerHook);
|
playerInjection.setPlayerHook(playerHook);
|
||||||
}
|
}
|
||||||
@ -298,6 +279,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
* @param plugin - plugin to check.
|
* @param plugin - plugin to check.
|
||||||
*/
|
*/
|
||||||
private void printPluginWarnings(Plugin plugin) {
|
private void printPluginWarnings(Plugin plugin) {
|
||||||
|
try {
|
||||||
switch (pluginVerifier.verify(plugin)) {
|
switch (pluginVerifier.verify(plugin)) {
|
||||||
case NO_DEPEND:
|
case NO_DEPEND:
|
||||||
reporter.reportWarning(this, Report.newBuilder(REPORT_PLUGIN_DEPEND_MISSING).messageParam(plugin.getName()));
|
reporter.reportWarning(this, Report.newBuilder(REPORT_PLUGIN_DEPEND_MISSING).messageParam(plugin.getName()));
|
||||||
@ -305,6 +287,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
reporter.reportWarning(this, Report.newBuilder(REPORT_PLUGIN_VERIFIER_ERROR).messageParam(e.getMessage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -702,6 +687,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
* @param manager - Bukkit plugin manager that provides player join/leave events.
|
* @param manager - Bukkit plugin manager that provides player join/leave events.
|
||||||
* @param plugin - the parent plugin.
|
* @param plugin - the parent plugin.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void registerEvents(PluginManager manager, final Plugin plugin) {
|
public void registerEvents(PluginManager manager, final Plugin plugin) {
|
||||||
if (spigotInjector != null && !spigotInjector.register(plugin))
|
if (spigotInjector != null && !spigotInjector.register(plugin))
|
||||||
throw new IllegalArgumentException("Spigot has already been registered.");
|
throw new IllegalArgumentException("Spigot has already been registered.");
|
||||||
@ -969,9 +955,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
return hasClosed;
|
return hasClosed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Called when ProtocolLib is closing.
|
|
||||||
*/
|
|
||||||
public void close() {
|
public void close() {
|
||||||
// Guard
|
// Guard
|
||||||
if (hasClosed)
|
if (hasClosed)
|
||||||
|
@ -5,7 +5,6 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.bukkit.plugin.PluginLoadOrder;
|
import org.bukkit.plugin.PluginLoadOrder;
|
||||||
|
|
||||||
@ -98,7 +97,7 @@ class PluginVerifier {
|
|||||||
* @return The retrieved plugin, or NULL if not found.
|
* @return The retrieved plugin, or NULL if not found.
|
||||||
*/
|
*/
|
||||||
private Plugin getPluginOrDefault(String pluginName) {
|
private Plugin getPluginOrDefault(String pluginName) {
|
||||||
return Bukkit.getPluginManager().getPlugin(pluginName);
|
return dependency.getServer().getPluginManager().getPlugin(pluginName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
package com.comphenix.protocol.injector.packet;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.ForwardingMap;
|
||||||
|
import com.google.common.collect.ForwardingMultimap;
|
||||||
|
import com.google.common.collect.HashMultimap;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
public class InverseMaps {
|
||||||
|
private InverseMaps() {
|
||||||
|
// Not constructable
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> Multimap<K, V> inverseMultimap(final Map<V, K> map, final Predicate<Map.Entry<V, K>> filter) {
|
||||||
|
final MapContainer container = new MapContainer(map);
|
||||||
|
|
||||||
|
return new ForwardingMultimap<K, V>() {
|
||||||
|
// The cached multimap
|
||||||
|
private Multimap<K, V> inverseMultimap;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Multimap<K, V> delegate() {
|
||||||
|
if (container.hasChanged()) {
|
||||||
|
inverseMultimap = HashMultimap.create();
|
||||||
|
|
||||||
|
// Construct the inverse map
|
||||||
|
for (Map.Entry<V, K> entry : map.entrySet()) {
|
||||||
|
if (filter.apply(entry)) {
|
||||||
|
inverseMultimap.put(entry.getValue(), entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container.setChanged(false);
|
||||||
|
}
|
||||||
|
return inverseMultimap;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> Map<K, V> inverseMap(final Map<V, K> map, final Predicate<Map.Entry<V, K>> filter) {
|
||||||
|
final MapContainer container = new MapContainer(map);
|
||||||
|
|
||||||
|
return new ForwardingMap<K, V>() {
|
||||||
|
// The cached map
|
||||||
|
private Map<K, V> inverseMap;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<K, V> delegate() {
|
||||||
|
if (container.hasChanged()) {
|
||||||
|
inverseMap = Maps.newHashMap();
|
||||||
|
|
||||||
|
// Construct the inverse map
|
||||||
|
for (Map.Entry<V, K> entry : map.entrySet()) {
|
||||||
|
if (filter.apply(entry)) {
|
||||||
|
inverseMap.put(entry.getValue(), entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container.setChanged(false);
|
||||||
|
}
|
||||||
|
return inverseMap;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a class that can detect if a map has changed.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
private static class MapContainer {
|
||||||
|
// For detecting changes
|
||||||
|
private Field modCountField;
|
||||||
|
private int lastModCount;
|
||||||
|
|
||||||
|
// The object along with whether or not this is the initial run
|
||||||
|
private Object source;
|
||||||
|
private boolean changed;
|
||||||
|
|
||||||
|
public MapContainer(Object source) {
|
||||||
|
this.source = source;
|
||||||
|
this.changed = true;
|
||||||
|
this.modCountField = FieldUtils.getField(source.getClass(), "modCount", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the map has changed.
|
||||||
|
* @return TRUE if it has, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasChanged() {
|
||||||
|
// Check if unchanged
|
||||||
|
checkChanged();
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark the map as changed or unchanged.
|
||||||
|
* @param changed - TRUE if the map has changed, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public void setChanged(boolean changed) {
|
||||||
|
this.changed = changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for modifications to the current map.
|
||||||
|
*/
|
||||||
|
protected void checkChanged() {
|
||||||
|
if (!changed) {
|
||||||
|
if (getModificationCount() != lastModCount) {
|
||||||
|
lastModCount = getModificationCount();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the current modification count.
|
||||||
|
* @return The current count, or something different than lastModCount if not accessible.
|
||||||
|
*/
|
||||||
|
private int getModificationCount() {
|
||||||
|
try {
|
||||||
|
return modCountField != null ? modCountField.getInt(source) : lastModCount + 1;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Unable to retrieve modCount.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,9 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import net.sf.cglib.proxy.Factory;
|
import net.sf.cglib.proxy.Factory;
|
||||||
|
|
||||||
@ -36,8 +39,10 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
|||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
import com.comphenix.protocol.wrappers.TroveWrapper;
|
import com.comphenix.protocol.wrappers.TroveWrapper;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static packet registry in Minecraft.
|
* Static packet registry in Minecraft.
|
||||||
@ -60,6 +65,10 @@ public class PacketRegistry {
|
|||||||
// The packet class to packet ID translator
|
// The packet class to packet ID translator
|
||||||
private static Map<Class, Integer> packetToID;
|
private static Map<Class, Integer> packetToID;
|
||||||
|
|
||||||
|
// Packet IDs to classes, grouped by whether or not they're vanilla or custom defined
|
||||||
|
private static Multimap<Integer, Class> customIdToPacket;
|
||||||
|
private static Map<Integer, Class> vanillaIdToPacket;
|
||||||
|
|
||||||
// Whether or not certain packets are sent by the client or the server
|
// Whether or not certain packets are sent by the client or the server
|
||||||
private static ImmutableSet<Integer> serverPackets;
|
private static ImmutableSet<Integer> serverPackets;
|
||||||
private static ImmutableSet<Integer> clientPackets;
|
private static ImmutableSet<Integer> clientPackets;
|
||||||
@ -93,8 +102,23 @@ public class PacketRegistry {
|
|||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new RuntimeException("Unable to retrieve the packetClassToIdMap", e);
|
throw new RuntimeException("Unable to retrieve the packetClassToIdMap", e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Create the inverse maps
|
||||||
|
customIdToPacket = InverseMaps.inverseMultimap(packetToID, new Predicate<Map.Entry<Class, Integer>>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(@Nullable Entry<Class, Integer> entry) {
|
||||||
|
return !MinecraftReflection.isMinecraftClass(entry.getKey());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// And the vanilla pack - here we assume a unique ID to class mapping
|
||||||
|
vanillaIdToPacket = InverseMaps.inverseMap(packetToID, new Predicate<Map.Entry<Class, Integer>>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(@Nullable Entry<Class, Integer> entry) {
|
||||||
|
return MinecraftReflection.isMinecraftClass(entry.getKey());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
return packetToID;
|
return packetToID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,23 +272,29 @@ public class PacketRegistry {
|
|||||||
* @return The associated class.
|
* @return The associated class.
|
||||||
*/
|
*/
|
||||||
public static Class getPacketClassFromID(int packetID, boolean forceVanilla) {
|
public static Class getPacketClassFromID(int packetID, boolean forceVanilla) {
|
||||||
|
|
||||||
Map<Integer, Class> lookup = forceVanilla ? previousValues : overwrittenPackets;
|
Map<Integer, Class> lookup = forceVanilla ? previousValues : overwrittenPackets;
|
||||||
|
Class<?> result = null;
|
||||||
|
|
||||||
// Optimized lookup
|
// Optimized lookup
|
||||||
if (lookup.containsKey(packetID)) {
|
if (lookup.containsKey(packetID)) {
|
||||||
return removeEnhancer(lookup.get(packetID), forceVanilla);
|
return removeEnhancer(lookup.get(packetID), forceVanilla);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will most likely not be used
|
// Refresh lookup tables
|
||||||
for (Map.Entry<Class, Integer> entry : getPacketToID().entrySet()) {
|
getPacketToID();
|
||||||
if (Objects.equal(entry.getValue(), packetID)) {
|
|
||||||
// Attempt to get the vanilla class here too
|
// See if we can look for non-vanilla classes
|
||||||
if (!forceVanilla || MinecraftReflection.isMinecraftClass(entry.getKey()))
|
if (!forceVanilla) {
|
||||||
return removeEnhancer(entry.getKey(), forceVanilla);
|
result = Iterables.getFirst(customIdToPacket.get(packetID), null);
|
||||||
}
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = vanillaIdToPacket.get(packetID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See if we got it
|
||||||
|
if (result != null)
|
||||||
|
return result;
|
||||||
|
else
|
||||||
throw new IllegalArgumentException("The packet ID " + packetID + " is not registered.");
|
throw new IllegalArgumentException("The packet ID " + packetID + " is not registered.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.comphenix.protocol.injector.packet;
|
package com.comphenix.protocol.injector.packet;
|
||||||
|
|
||||||
|
import java.io.DataInput;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@ -33,6 +34,8 @@ import net.sf.cglib.proxy.NoOp;
|
|||||||
|
|
||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.Packets;
|
||||||
import com.comphenix.protocol.error.ErrorReporter;
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
|
import com.comphenix.protocol.error.Report;
|
||||||
|
import com.comphenix.protocol.error.ReportType;
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
@ -50,6 +53,8 @@ import com.comphenix.protocol.utility.MinecraftReflection;
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
class ProxyPacketInjector implements PacketInjector {
|
class ProxyPacketInjector implements PacketInjector {
|
||||||
|
public static final ReportType REPORT_CANNOT_FIND_READ_PACKET_METHOD = new ReportType("Cannot find read packet method for ID %s.");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a way to update the packet ID to class lookup table.
|
* Represents a way to update the packet ID to class lookup table.
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
@ -134,7 +139,7 @@ class ProxyPacketInjector implements PacketInjector {
|
|||||||
*/
|
*/
|
||||||
private static FuzzyMethodContract readPacket = FuzzyMethodContract.newBuilder().
|
private static FuzzyMethodContract readPacket = FuzzyMethodContract.newBuilder().
|
||||||
returnTypeVoid().
|
returnTypeVoid().
|
||||||
parameterExactType(DataInputStream.class).
|
parameterDerivedOf(DataInput.class).
|
||||||
parameterCount(1).
|
parameterCount(1).
|
||||||
build();
|
build();
|
||||||
|
|
||||||
@ -155,6 +160,9 @@ class ProxyPacketInjector implements PacketInjector {
|
|||||||
// Share callback filter
|
// Share callback filter
|
||||||
private CallbackFilter filter;
|
private CallbackFilter filter;
|
||||||
|
|
||||||
|
// Determine if the read packet method was found
|
||||||
|
private boolean readPacketIntercepted = false;
|
||||||
|
|
||||||
public ProxyPacketInjector(ClassLoader classLoader, ListenerInvoker manager,
|
public ProxyPacketInjector(ClassLoader classLoader, ListenerInvoker manager,
|
||||||
PlayerInjectionHandler playerInjection, ErrorReporter reporter) throws FieldAccessException {
|
PlayerInjectionHandler playerInjection, ErrorReporter reporter) throws FieldAccessException {
|
||||||
|
|
||||||
@ -224,17 +232,21 @@ class ProxyPacketInjector implements PacketInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (filter == null) {
|
if (filter == null) {
|
||||||
|
readPacketIntercepted = false;
|
||||||
|
|
||||||
filter = new CallbackFilter() {
|
filter = new CallbackFilter() {
|
||||||
@Override
|
@Override
|
||||||
public int accept(Method method) {
|
public int accept(Method method) {
|
||||||
// Skip methods defined in Object
|
// Skip methods defined in Object
|
||||||
if (method.getDeclaringClass().equals(Object.class))
|
if (method.getDeclaringClass().equals(Object.class)) {
|
||||||
return 0;
|
return 0;
|
||||||
else if (readPacket.isMatch(MethodInfo.fromMethod(method), null))
|
} else if (readPacket.isMatch(MethodInfo.fromMethod(method), null)) {
|
||||||
|
readPacketIntercepted = true;
|
||||||
return 1;
|
return 1;
|
||||||
else
|
} else {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +264,12 @@ class ProxyPacketInjector implements PacketInjector {
|
|||||||
// Add a static reference
|
// Add a static reference
|
||||||
Enhancer.registerStaticCallbacks(proxy, new Callback[] { NoOp.INSTANCE, modifierReadPacket, modifierRest });
|
Enhancer.registerStaticCallbacks(proxy, new Callback[] { NoOp.INSTANCE, modifierReadPacket, modifierRest });
|
||||||
|
|
||||||
|
// Check that we found the read method
|
||||||
|
if (!readPacketIntercepted) {
|
||||||
|
reporter.reportWarning(this,
|
||||||
|
Report.newBuilder(REPORT_CANNOT_FIND_READ_PACKET_METHOD).messageParam(packetID));
|
||||||
|
}
|
||||||
|
|
||||||
// Override values
|
// Override values
|
||||||
previous.put(packetID, old);
|
previous.put(packetID, old);
|
||||||
registry.put(proxy, packetID);
|
registry.put(proxy, packetID);
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
package com.comphenix.protocol.injector.player;
|
package com.comphenix.protocol.injector.player;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -30,6 +31,7 @@ import com.comphenix.protocol.error.ErrorReporter;
|
|||||||
import com.comphenix.protocol.error.Report;
|
import com.comphenix.protocol.error.Report;
|
||||||
import com.comphenix.protocol.error.ReportType;
|
import com.comphenix.protocol.error.ReportType;
|
||||||
import com.comphenix.protocol.injector.server.AbstractInputStreamLookup;
|
import com.comphenix.protocol.injector.server.AbstractInputStreamLookup;
|
||||||
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.comphenix.protocol.reflect.ObjectWriter;
|
import com.comphenix.protocol.reflect.ObjectWriter;
|
||||||
@ -41,7 +43,7 @@ import com.comphenix.protocol.utility.MinecraftReflection;
|
|||||||
*
|
*
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
class InjectedServerConnection {
|
public class InjectedServerConnection {
|
||||||
// A number of things can go wrong ...
|
// A number of things can go wrong ...
|
||||||
public static final ReportType REPORT_CANNOT_FIND_MINECRAFT_SERVER = new ReportType("Cannot extract minecraft server from Bukkit.");
|
public static final ReportType REPORT_CANNOT_FIND_MINECRAFT_SERVER = new ReportType("Cannot extract minecraft server from Bukkit.");
|
||||||
public static final ReportType REPORT_CANNOT_INJECT_SERVER_CONNECTION = new ReportType("Cannot inject into server connection. Bad things will happen.");
|
public static final ReportType REPORT_CANNOT_INJECT_SERVER_CONNECTION = new ReportType("Cannot inject into server connection. Bad things will happen.");
|
||||||
@ -66,12 +68,21 @@ class InjectedServerConnection {
|
|||||||
private List<VolatileField> listFields;
|
private List<VolatileField> listFields;
|
||||||
private List<ReplacedArrayList<Object>> replacedLists;
|
private List<ReplacedArrayList<Object>> replacedLists;
|
||||||
|
|
||||||
|
// The current detected server socket
|
||||||
|
public enum ServerSocketType {
|
||||||
|
SERVER_CONNECTION,
|
||||||
|
LISTENER_THREAD,
|
||||||
|
}
|
||||||
|
|
||||||
// Used to inject net handlers
|
// Used to inject net handlers
|
||||||
private NetLoginInjector netLoginInjector;
|
private NetLoginInjector netLoginInjector;
|
||||||
|
|
||||||
// Inject server connections
|
// Inject server connections
|
||||||
private AbstractInputStreamLookup socketInjector;
|
private AbstractInputStreamLookup socketInjector;
|
||||||
|
|
||||||
|
// Detected by the initializer
|
||||||
|
private ServerSocketType socketType;
|
||||||
|
|
||||||
private Server server;
|
private Server server;
|
||||||
private ErrorReporter reporter;
|
private ErrorReporter reporter;
|
||||||
private boolean hasAttempted;
|
private boolean hasAttempted;
|
||||||
@ -88,7 +99,31 @@ class InjectedServerConnection {
|
|||||||
this.netLoginInjector = netLoginInjector;
|
this.netLoginInjector = netLoginInjector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void injectList() {
|
/**
|
||||||
|
* Retrieve the current server connection.
|
||||||
|
* @param reporter - error reproter.
|
||||||
|
* @param server - the current server.
|
||||||
|
* @return The current server connection, or NULL if it hasn't been initialized yet.
|
||||||
|
* @throws FieldAccessException Reflection error.
|
||||||
|
*/
|
||||||
|
public static Object getServerConnection(ErrorReporter reporter, Server server) {
|
||||||
|
try {
|
||||||
|
// Now we are probably able to check for Netty
|
||||||
|
InjectedServerConnection inspector = new InjectedServerConnection(reporter, null, server, null);
|
||||||
|
return inspector.getServerConnection();
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new FieldAccessException("Reflection error.", e);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new FieldAccessException("Corrupt data.", e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new FieldAccessException("Minecraft error.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initial reflective detective work. Will be automatically called by most methods in this class.
|
||||||
|
*/
|
||||||
|
public void initialize() {
|
||||||
// Only execute this method once
|
// Only execute this method once
|
||||||
if (!hasAttempted)
|
if (!hasAttempted)
|
||||||
hasAttempted = true;
|
hasAttempted = true;
|
||||||
@ -112,12 +147,11 @@ class InjectedServerConnection {
|
|||||||
getMethodByParameters("getServerConnection",
|
getMethodByParameters("getServerConnection",
|
||||||
MinecraftReflection.getServerConnectionClass(), new Class[] {});
|
MinecraftReflection.getServerConnectionClass(), new Class[] {});
|
||||||
// We're using Minecraft 1.3.1
|
// We're using Minecraft 1.3.1
|
||||||
injectServerConnection();
|
socketType = ServerSocketType.SERVER_CONNECTION;
|
||||||
|
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
|
||||||
// Minecraft 1.2.5 or lower
|
// Minecraft 1.2.5 or lower
|
||||||
injectListenerThread();
|
socketType = ServerSocketType.LISTENER_THREAD;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Oh damn - inform the player
|
// Oh damn - inform the player
|
||||||
@ -125,11 +159,77 @@ class InjectedServerConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void injectListenerThread() {
|
/**
|
||||||
try {
|
* Retrieve the known server socket type.
|
||||||
|
* <p>
|
||||||
|
* This depends on the version of CraftBukkit we are using.
|
||||||
|
* @return The server socket type.
|
||||||
|
*/
|
||||||
|
public ServerSocketType getServerSocketType() {
|
||||||
|
return socketType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject the connection interceptor into the correct server socket implementation.
|
||||||
|
*/
|
||||||
|
public void injectList() {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
if (socketType == ServerSocketType.SERVER_CONNECTION) {
|
||||||
|
injectServerConnection();
|
||||||
|
} else if (socketType == ServerSocketType.LISTENER_THREAD) {
|
||||||
|
injectListenerThread();
|
||||||
|
} else {
|
||||||
|
// Damn it
|
||||||
|
throw new IllegalStateException("Unable to detected server connection.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the listener thread field.
|
||||||
|
*/
|
||||||
|
private void initializeListenerField() {
|
||||||
if (listenerThreadField == null)
|
if (listenerThreadField == null)
|
||||||
listenerThreadField = FuzzyReflection.fromObject(minecraftServer).
|
listenerThreadField = FuzzyReflection.fromObject(minecraftServer).
|
||||||
getFieldByType("networkListenThread", MinecraftReflection.getNetworkListenThreadClass());
|
getFieldByType("networkListenThread", MinecraftReflection.getNetworkListenThreadClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the listener thread object, or NULL the server isn't using this socket implementation.
|
||||||
|
* @return The listener thread, or NULL.
|
||||||
|
* @throws IllegalAccessException Cannot access field.
|
||||||
|
* @hrows RuntimeException Unexpected class structure - the field doesn't exist.
|
||||||
|
*/
|
||||||
|
public Object getListenerThread() throws RuntimeException, IllegalAccessException {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
if (socketType == ServerSocketType.LISTENER_THREAD) {
|
||||||
|
initializeListenerField();
|
||||||
|
return listenerThreadField.get(minecraftServer);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the server connection object, or NULL if the server isn't using it as the socket implementation.
|
||||||
|
* @return The socket connection, or NULL.
|
||||||
|
* @throws IllegalAccessException If the reflective operation failed.
|
||||||
|
* @throws IllegalArgumentException If the reflective operation failed.
|
||||||
|
* @throws InvocationTargetException If the reflective operation failed.
|
||||||
|
*/
|
||||||
|
public Object getServerConnection() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
if (socketType == ServerSocketType.SERVER_CONNECTION)
|
||||||
|
return serverConnectionMethod.invoke(minecraftServer);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void injectListenerThread() {
|
||||||
|
try {
|
||||||
|
initializeListenerField();
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
reporter.reportDetailed(this,
|
reporter.reportDetailed(this,
|
||||||
Report.newBuilder(REPORT_CANNOT_FIND_LISTENER_THREAD).callerParam(minecraftServer).error(e)
|
Report.newBuilder(REPORT_CANNOT_FIND_LISTENER_THREAD).callerParam(minecraftServer).error(e)
|
||||||
@ -141,7 +241,7 @@ class InjectedServerConnection {
|
|||||||
|
|
||||||
// Attempt to get the thread
|
// Attempt to get the thread
|
||||||
try {
|
try {
|
||||||
listenerThread = listenerThreadField.get(minecraftServer);
|
listenerThread = getListenerThread();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_READ_LISTENER_THREAD).error(e));
|
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_READ_LISTENER_THREAD).error(e));
|
||||||
return;
|
return;
|
||||||
@ -160,7 +260,7 @@ class InjectedServerConnection {
|
|||||||
|
|
||||||
// Careful - we might fail
|
// Careful - we might fail
|
||||||
try {
|
try {
|
||||||
serverConnection = serverConnectionMethod.invoke(minecraftServer);
|
serverConnection = getServerConnection();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
reporter.reportDetailed(this,
|
reporter.reportDetailed(this,
|
||||||
Report.newBuilder(REPORT_CANNOT_FIND_SERVER_CONNECTION).callerParam(minecraftServer).error(e)
|
Report.newBuilder(REPORT_CANNOT_FIND_SERVER_CONNECTION).callerParam(minecraftServer).error(e)
|
||||||
|
@ -161,9 +161,4 @@ public interface PlayerInjectionHandler {
|
|||||||
* Close any lingering proxy injections.
|
* Close any lingering proxy injections.
|
||||||
*/
|
*/
|
||||||
public abstract void close();
|
public abstract void close();
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform any action that must be delayed until the world(s) has loaded.
|
|
||||||
*/
|
|
||||||
public abstract void postWorldLoaded();
|
|
||||||
}
|
}
|
@ -284,7 +284,7 @@ public abstract class PlayerInjector implements SocketInjector {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the associated remote address of a player.
|
* Retrieve the associated remote address of a player.
|
||||||
* @return The associated remote address..
|
* @return The associated remote address.
|
||||||
* @throws IllegalAccessException If we're unable to read the socket address field.
|
* @throws IllegalAccessException If we're unable to read the socket address field.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -21,6 +21,7 @@ import java.io.DataInputStream;
|
|||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -51,6 +52,7 @@ import com.comphenix.protocol.injector.server.SocketInjector;
|
|||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
@ -143,14 +145,6 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
|||||||
serverInjection.injectList();
|
serverInjection.injectList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postWorldLoaded() {
|
|
||||||
// This will actually create a socket and a seperate thread ...
|
|
||||||
if (inputStreamLookup != null) {
|
|
||||||
inputStreamLookup.postWorldLoaded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves how the server packets are read.
|
* Retrieves how the server packets are read.
|
||||||
* @return Injection method for reading server packets.
|
* @return Injection method for reading server packets.
|
||||||
@ -350,6 +344,7 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
SocketInjector previous = inputStreamLookup.peekSocketInjector(address);
|
SocketInjector previous = inputStreamLookup.peekSocketInjector(address);
|
||||||
|
Socket socket = injector.getSocket();
|
||||||
|
|
||||||
// Close any previously associated hooks before we proceed
|
// Close any previously associated hooks before we proceed
|
||||||
if (previous != null && !(player instanceof Factory)) {
|
if (previous != null && !(player instanceof Factory)) {
|
||||||
@ -363,8 +358,7 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
|||||||
}
|
}
|
||||||
injector.injectManager();
|
injector.injectManager();
|
||||||
|
|
||||||
// Save injector
|
saveAddressLookup(address, socket, injector);
|
||||||
inputStreamLookup.setSocketInjector(address, injector);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,6 +407,17 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
|||||||
return injector;
|
return injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saveAddressLookup(SocketAddress address, Socket socket, SocketInjector injector) {
|
||||||
|
SocketAddress socketAddress = socket != null ? socket.getRemoteSocketAddress() : null;
|
||||||
|
|
||||||
|
if (socketAddress != null && !Objects.equal(socketAddress, address)) {
|
||||||
|
// Save this version as well
|
||||||
|
inputStreamLookup.setSocketInjector(socketAddress, injector);
|
||||||
|
}
|
||||||
|
// Save injector
|
||||||
|
inputStreamLookup.setSocketInjector(address, injector);
|
||||||
|
}
|
||||||
|
|
||||||
private void cleanupHook(PlayerInjector injector) {
|
private void cleanupHook(PlayerInjector injector) {
|
||||||
// Clean up as much as possible
|
// Clean up as much as possible
|
||||||
try {
|
try {
|
||||||
|
@ -26,11 +26,6 @@ public abstract class AbstractInputStreamLookup {
|
|||||||
*/
|
*/
|
||||||
public abstract void inject(Object container);
|
public abstract void inject(Object container);
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked when the world has loaded.
|
|
||||||
*/
|
|
||||||
public abstract void postWorldLoaded();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the associated socket injector for a player.
|
* Retrieve the associated socket injector for a player.
|
||||||
* @param input - the indentifying filtered input stream.
|
* @param input - the indentifying filtered input stream.
|
||||||
|
@ -10,36 +10,14 @@ import java.util.List;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
public class BukkitSocketInjector implements SocketInjector {
|
public class BukkitSocketInjector implements SocketInjector {
|
||||||
/**
|
|
||||||
* Represents a single send packet command.
|
|
||||||
* @author Kristian
|
|
||||||
*/
|
|
||||||
static class SendPacketCommand {
|
|
||||||
private final Object packet;
|
|
||||||
private final boolean filtered;
|
|
||||||
|
|
||||||
public SendPacketCommand(Object packet, boolean filtered) {
|
|
||||||
this.packet = packet;
|
|
||||||
this.filtered = filtered;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getPacket() {
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFiltered() {
|
|
||||||
return filtered;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Player player;
|
private Player player;
|
||||||
|
|
||||||
// Queue of server packets
|
// Queue of server packets
|
||||||
private List<SendPacketCommand> syncronizedQueue = Collections.synchronizedList(new ArrayList<SendPacketCommand>());
|
private List<QueuedSendPacket> syncronizedQueue = Collections.synchronizedList(new ArrayList<QueuedSendPacket>());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a temporary socket injector.
|
* Represents a temporary socket injector.
|
||||||
* @param temporaryPlayer -
|
* @param temporaryPlayer - a temporary player.
|
||||||
*/
|
*/
|
||||||
public BukkitSocketInjector(Player player) {
|
public BukkitSocketInjector(Player player) {
|
||||||
if (player == null)
|
if (player == null)
|
||||||
@ -65,7 +43,7 @@ public class BukkitSocketInjector implements SocketInjector {
|
|||||||
@Override
|
@Override
|
||||||
public void sendServerPacket(Object packet, boolean filtered)
|
public void sendServerPacket(Object packet, boolean filtered)
|
||||||
throws InvocationTargetException {
|
throws InvocationTargetException {
|
||||||
SendPacketCommand command = new SendPacketCommand(packet, filtered);
|
QueuedSendPacket command = new QueuedSendPacket(packet, filtered);
|
||||||
|
|
||||||
// Queue until we can find something better
|
// Queue until we can find something better
|
||||||
syncronizedQueue.add(command);
|
syncronizedQueue.add(command);
|
||||||
@ -86,7 +64,7 @@ public class BukkitSocketInjector implements SocketInjector {
|
|||||||
// Transmit all queued packets to a different injector.
|
// Transmit all queued packets to a different injector.
|
||||||
try {
|
try {
|
||||||
synchronized(syncronizedQueue) {
|
synchronized(syncronizedQueue) {
|
||||||
for (SendPacketCommand command : syncronizedQueue) {
|
for (QueuedSendPacket command : syncronizedQueue) {
|
||||||
delegate.sendServerPacket(command.getPacket(), command.isFiltered());
|
delegate.sendServerPacket(command.getPacket(), command.isFiltered());
|
||||||
}
|
}
|
||||||
syncronizedQueue.clear();
|
syncronizedQueue.clear();
|
||||||
|
@ -53,11 +53,6 @@ class InputStreamReflectLookup extends AbstractInputStreamLookup {
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postWorldLoaded() {
|
|
||||||
// Nothing again
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SocketInjector peekSocketInjector(SocketAddress address) {
|
public SocketInjector peekSocketInjector(SocketAddress address) {
|
||||||
try {
|
try {
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.comphenix.protocol.injector.server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single send packet command.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
class QueuedSendPacket {
|
||||||
|
private final Object packet;
|
||||||
|
private final boolean filtered;
|
||||||
|
|
||||||
|
public QueuedSendPacket(Object packet, boolean filtered) {
|
||||||
|
this.packet = packet;
|
||||||
|
this.filtered = filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the underlying packet that will be sent.
|
||||||
|
* @return The underlying packet.
|
||||||
|
*/
|
||||||
|
public Object getPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the packet should be intercepted by packet listeners.
|
||||||
|
* @return TRUE if it should, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isFiltered() {
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
}
|
@ -115,11 +115,6 @@ class DummyPlayerHandler implements PlayerInjectionHandler {
|
|||||||
// Yes, really
|
// Yes, really
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postWorldLoaded() {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updatePlayer(Player player) {
|
public void updatePlayer(Player player) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
@ -161,8 +161,8 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
|||||||
/**
|
/**
|
||||||
* Add a new required parameter whose type must be a superclass of the given type.
|
* Add a new required parameter whose type must be a superclass of the given type.
|
||||||
* <p>
|
* <p>
|
||||||
* If a parameter is of type Number, any derived class (Integer, Long, etc.) will match it.
|
* If a method parameter is of type Number, then any derived class here (Integer, Long, etc.) will match it.
|
||||||
* @param type - a type or derived type of the matching parameter.
|
* @param type - a type or less derived type of the matching parameter.
|
||||||
* @return This builder, for chaining.
|
* @return This builder, for chaining.
|
||||||
*/
|
*/
|
||||||
public Builder parameterSuperOf(Class<?> type) {
|
public Builder parameterSuperOf(Class<?> type) {
|
||||||
@ -170,6 +170,18 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new required parameter whose type must be a derived class of the given class.
|
||||||
|
* <p>
|
||||||
|
* If the method parameter has the type Integer, then the class Number here will match it.
|
||||||
|
* @param type - a type or more derived type of the matching parameter.
|
||||||
|
* @return This builder, for chaining.
|
||||||
|
*/
|
||||||
|
public Builder parameterDerivedOf(Class<?> type) {
|
||||||
|
member.paramMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchDerived(type)));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new required parameter whose type must match the given class matcher.
|
* Add a new required parameter whose type must match the given class matcher.
|
||||||
* @param classMatcher - the class matcher.
|
* @param classMatcher - the class matcher.
|
||||||
@ -204,6 +216,19 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new required parameter whose type must be a derived class of the given class.
|
||||||
|
* <p>
|
||||||
|
* If the method parameter has the type Integer, then the class Number here will match it.
|
||||||
|
* @param type - a type or more derived type of the matching parameter.
|
||||||
|
* @param index - the expected position in the parameter list.
|
||||||
|
* @return This builder, for chaining.
|
||||||
|
*/
|
||||||
|
public Builder parameterDerivedOf(Class<?> type, int index) {
|
||||||
|
member.paramMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchDerived(type), index));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new required parameter whose type must match the given class matcher and index.
|
* Add a new required parameter whose type must match the given class matcher and index.
|
||||||
* @param classMatcher - the class matcher.
|
* @param classMatcher - the class matcher.
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
package com.comphenix.protocol.utility;
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutput;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@ -219,6 +219,11 @@ public class MinecraftReflection {
|
|||||||
MINECRAFT_FULL_PACKAGE = minecraftPackage;
|
MINECRAFT_FULL_PACKAGE = minecraftPackage;
|
||||||
CRAFTBUKKIT_PACKAGE = craftBukkitPackage;
|
CRAFTBUKKIT_PACKAGE = craftBukkitPackage;
|
||||||
|
|
||||||
|
// Make sure it exists
|
||||||
|
if (getMinecraftServerClass() == null) {
|
||||||
|
throw new IllegalArgumentException("Cannot find MinecraftServer for package " + minecraftPackage);
|
||||||
|
}
|
||||||
|
|
||||||
// Standard matcher
|
// Standard matcher
|
||||||
setDynamicPackageMatcher(MINECRAFT_OBJECT);
|
setDynamicPackageMatcher(MINECRAFT_OBJECT);
|
||||||
}
|
}
|
||||||
@ -792,7 +797,7 @@ public class MinecraftReflection {
|
|||||||
Method selected = FuzzyReflection.fromClass(getDataWatcherClass(), true).
|
Method selected = FuzzyReflection.fromClass(getDataWatcherClass(), true).
|
||||||
getMethod(FuzzyMethodContract.newBuilder().
|
getMethod(FuzzyMethodContract.newBuilder().
|
||||||
requireModifier(Modifier.STATIC).
|
requireModifier(Modifier.STATIC).
|
||||||
parameterSuperOf(DataOutputStream.class, 0).
|
parameterDerivedOf(DataOutput.class, 0).
|
||||||
parameterMatches(getMinecraftObjectMatcher(), 1).
|
parameterMatches(getMinecraftObjectMatcher(), 1).
|
||||||
build());
|
build());
|
||||||
|
|
||||||
|
@ -2,7 +2,9 @@ package com.comphenix.protocol.utility;
|
|||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInput;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutput;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -13,6 +15,7 @@ import org.bukkit.inventory.ItemStack;
|
|||||||
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
|
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
|
||||||
|
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods for reading and writing Minecraft objects to streams.
|
* Utility methods for reading and writing Minecraft objects to streams.
|
||||||
@ -37,11 +40,14 @@ public class StreamSerializer {
|
|||||||
public ItemStack deserializeItemStack(@Nonnull DataInputStream input) throws IOException {
|
public ItemStack deserializeItemStack(@Nonnull DataInputStream input) throws IOException {
|
||||||
if (input == null)
|
if (input == null)
|
||||||
throw new IllegalArgumentException("Input stream cannot be NULL.");
|
throw new IllegalArgumentException("Input stream cannot be NULL.");
|
||||||
if (readItemMethod == null)
|
if (readItemMethod == null) {
|
||||||
readItemMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
|
readItemMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||||
getMethodByParameters("readPacket",
|
FuzzyMethodContract.newBuilder().
|
||||||
MinecraftReflection.getItemStackClass(),
|
parameterCount(1).
|
||||||
new Class<?>[] {DataInputStream.class});
|
parameterDerivedOf(DataInput.class).
|
||||||
|
returnDerivedOf(MinecraftReflection.getItemStackClass()).
|
||||||
|
build());
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
Object nmsItem = readItemMethod.invoke(null, input);
|
Object nmsItem = readItemMethod.invoke(null, input);
|
||||||
|
|
||||||
@ -88,10 +94,12 @@ public class StreamSerializer {
|
|||||||
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
|
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
|
||||||
|
|
||||||
if (writeItemMethod == null)
|
if (writeItemMethod == null)
|
||||||
writeItemMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
|
writeItemMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||||
getMethodByParameters("writePacket", new Class<?>[] {
|
FuzzyMethodContract.newBuilder().
|
||||||
MinecraftReflection.getItemStackClass(),
|
parameterCount(2).
|
||||||
DataOutputStream.class });
|
parameterDerivedOf(MinecraftReflection.getItemStackClass(), 0).
|
||||||
|
parameterDerivedOf(DataOutput.class, 1).
|
||||||
|
build());
|
||||||
try {
|
try {
|
||||||
writeItemMethod.invoke(null, nmsItem, output);
|
writeItemMethod.invoke(null, nmsItem, output);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name: ProtocolLib
|
name: ProtocolLib
|
||||||
version: 2.4.5
|
version: 2.4.8-SNAPSHOT
|
||||||
description: Provides read/write access to the Minecraft protocol.
|
description: Provides read/write access to the Minecraft protocol.
|
||||||
author: Comphenix
|
author: Comphenix
|
||||||
website: http://www.comphenix.net/ProtocolLib
|
website: http://www.comphenix.net/ProtocolLib
|
||||||
|
@ -4,10 +4,10 @@ import static org.mockito.Matchers.any;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import net.minecraft.server.v1_5_R2.StatisticList;
|
import net.minecraft.server.v1_6_R2.StatisticList;
|
||||||
|
|
||||||
// Will have to be updated for every version though
|
// Will have to be updated for every version though
|
||||||
import org.bukkit.craftbukkit.v1_5_R2.inventory.CraftItemFactory;
|
import org.bukkit.craftbukkit.v1_6_R2.inventory.CraftItemFactory;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
@ -63,6 +63,6 @@ public class BukkitInitialization {
|
|||||||
*/
|
*/
|
||||||
public static void initializePackage() {
|
public static void initializePackage() {
|
||||||
// Initialize reflection
|
// Initialize reflection
|
||||||
MinecraftReflection.setMinecraftPackage("net.minecraft.server.v1_5_R2", "org.bukkit.craftbukkit.v1_5_R2");
|
MinecraftReflection.setMinecraftPackage("net.minecraft.server.v1_6_R2", "org.bukkit.craftbukkit.v1_6_R2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,9 @@ import static org.junit.Assert.*;
|
|||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.SerializationUtils;
|
||||||
// Will have to be updated for every version though
|
// Will have to be updated for every version though
|
||||||
import org.bukkit.craftbukkit.v1_5_R2.inventory.CraftItemFactory;
|
import org.bukkit.craftbukkit.v1_6_R2.inventory.CraftItemFactory;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.WorldType;
|
import org.bukkit.WorldType;
|
||||||
@ -306,6 +307,17 @@ public class PacketContainerTest {
|
|||||||
assertEquals(list, watchableAccessor.read(0));
|
assertEquals(list, watchableAccessor.read(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerialization() {
|
||||||
|
PacketContainer chat = new PacketContainer(3);
|
||||||
|
chat.getStrings().write(0, "Test");
|
||||||
|
|
||||||
|
PacketContainer copy = (PacketContainer) SerializationUtils.clone(chat);
|
||||||
|
|
||||||
|
assertEquals(3, copy.getID());
|
||||||
|
assertEquals("Test", copy.getStrings().read(0));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeepClone() {
|
public void testDeepClone() {
|
||||||
// Try constructing all the packets
|
// Try constructing all the packets
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.plugin.PluginDescriptionFile;
|
||||||
|
import org.bukkit.plugin.PluginLoadOrder;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||||
|
import com.comphenix.protocol.injector.PluginVerifier.VerificationResult;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
// Damn final classes
|
||||||
|
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
|
||||||
|
@PrepareForTest(PluginDescriptionFile.class)
|
||||||
|
public class PluginVerifierTest {
|
||||||
|
@Test
|
||||||
|
public void testDependecies() {
|
||||||
|
List<Plugin> plugins = Lists.newArrayList();
|
||||||
|
Server server = mockServer(plugins);
|
||||||
|
|
||||||
|
Plugin library = mockPlugin(server, "ProtocolLib", PluginLoadOrder.POSTWORLD);
|
||||||
|
Plugin skillPlugin = mockPlugin(server, "SkillPlugin", "RaidCraft-API", "RCPermissions", "RCConversations");
|
||||||
|
Plugin raidCraftAPI = mockPlugin(server, "RaidCraft-API", "WorldGuard", "WorldEdit");
|
||||||
|
Plugin conversations = mockPlugin(server, "RCConversations", "RaidCraft-API");
|
||||||
|
Plugin permissions = mockPlugin(server, "RCPermissions", "RaidCraft-API");
|
||||||
|
|
||||||
|
// Add the plugins
|
||||||
|
plugins.addAll(Arrays.asList(library, skillPlugin, raidCraftAPI, conversations, permissions));
|
||||||
|
PluginVerifier verifier = new PluginVerifier(library);
|
||||||
|
|
||||||
|
// Verify the root - it should have no dependencies on ProtocolLib
|
||||||
|
assertEquals(VerificationResult.NO_DEPEND, verifier.verify(skillPlugin));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Server mockServer(final List<Plugin> plugins) {
|
||||||
|
Server mockServer = mock(Server.class);
|
||||||
|
PluginManager manager = mock(PluginManager.class);
|
||||||
|
|
||||||
|
when(mockServer.getPluginManager()).thenReturn(manager);
|
||||||
|
when(manager.getPlugin(anyString())).thenAnswer(new Answer<Plugin>() {
|
||||||
|
@Override
|
||||||
|
public Plugin answer(InvocationOnMock invocation) throws Throwable {
|
||||||
|
String name = (String) invocation.getArguments()[0];
|
||||||
|
|
||||||
|
for (Plugin plugin : plugins) {
|
||||||
|
if (Objects.equal(name, plugin.getName())) {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return mockServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Plugin mockPlugin(Server server, String name,String... depend) {
|
||||||
|
return mockPlugin(server, name, PluginLoadOrder.POSTWORLD, depend);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Plugin mockPlugin(Server server, String name, PluginLoadOrder order, String... depend) {
|
||||||
|
Plugin plugin = mock(Plugin.class);
|
||||||
|
PluginDescriptionFile file = mock(PluginDescriptionFile.class);
|
||||||
|
|
||||||
|
when(plugin.getServer()).thenReturn(server);
|
||||||
|
when(plugin.getName()).thenReturn(name);
|
||||||
|
when(plugin.toString()).thenReturn(name);
|
||||||
|
when(plugin.getDescription()).thenReturn(file);
|
||||||
|
|
||||||
|
// This is the difficult part
|
||||||
|
when(file.getLoad()).thenReturn(order);
|
||||||
|
when(file.getDepend()).thenReturn(Arrays.asList(depend));
|
||||||
|
when(file.getSoftDepend()).thenReturn(null);
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.craftbukkit.v1_6_R2.inventory.CraftItemFactory;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.BukkitInitialization;
|
||||||
|
|
||||||
|
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
|
||||||
|
@PrepareForTest(CraftItemFactory.class)
|
||||||
|
public class StreamSerializerTest {
|
||||||
|
@BeforeClass
|
||||||
|
public static void initializeBukkit() throws IllegalAccessException {
|
||||||
|
BukkitInitialization.initializeItemMeta();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerializer() throws IOException {
|
||||||
|
ItemStack before = new ItemStack(Material.GOLD_AXE);
|
||||||
|
|
||||||
|
StreamSerializer serializer = new StreamSerializer();
|
||||||
|
String data = serializer.serializeItemStack(before);
|
||||||
|
ItemStack after = serializer.deserializeItemStack(data);
|
||||||
|
|
||||||
|
assertEquals(before.getType(), after.getType());
|
||||||
|
assertEquals(before.getAmount(), after.getAmount());
|
||||||
|
}
|
||||||
|
}
|
In neuem Issue referenzieren
Einen Benutzer sperren