Merge branch 'master' into gh-pages

This commit is contained in:
Kristian S. Stangeland 2012-12-10 16:43:44 +01:00
commit 3d166ae353
41 changed files with 1290 additions and 319 deletions

View File

@ -4,7 +4,7 @@
<groupId>com.comphenix.protocol</groupId> <groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId> <artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name> <name>ProtocolLib</name>
<version>1.7.1</version> <version>1.8.0</version>
<description>Provides read/write access to the Minecraft protocol.</description> <description>Provides read/write access to the Minecraft protocol.</description>
<url>http://dev.bukkit.org/server-mods/protocollib/</url> <url>http://dev.bukkit.org/server-mods/protocollib/</url>
<developers> <developers>
@ -139,7 +139,7 @@
<dependency> <dependency>
<groupId>org.bukkit</groupId> <groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId> <artifactId>craftbukkit</artifactId>
<version>1.3.2-R1.0</version> <version>1.4.5-R0.3-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -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>1.7.1</version> <version>1.8.0</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>
@ -202,7 +202,7 @@
<dependency> <dependency>
<groupId>org.bukkit</groupId> <groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId> <artifactId>craftbukkit</artifactId>
<version>1.3.2-R1.0</version> <version>1.4.5-R0.3-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -10,7 +10,6 @@ import java.util.WeakHashMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.Factory;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@ -29,6 +28,7 @@ import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.PrettyPrinter; import com.comphenix.protocol.reflect.PrettyPrinter;
import com.comphenix.protocol.utility.ChatExtensions; import com.comphenix.protocol.utility.ChatExtensions;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.DiscreteDomains; import com.google.common.collect.DiscreteDomains;
import com.google.common.collect.Range; import com.google.common.collect.Range;
import com.google.common.collect.Ranges; import com.google.common.collect.Ranges;
@ -394,7 +394,7 @@ class CommandPacket extends CommandBase {
// Detailed will print the packet's content too // Detailed will print the packet's content too
if (detailed) { if (detailed) {
try { try {
Packet packet = event.getPacket().getHandle(); Object packet = event.getPacket().getHandle();
Class<?> clazz = packet.getClass(); Class<?> clazz = packet.getClass();
// Get the first Minecraft super class // Get the first Minecraft super class
@ -404,7 +404,7 @@ class CommandPacket extends CommandBase {
} }
logger.info(shortDescription + ":\n" + logger.info(shortDescription + ":\n" +
PrettyPrinter.printObject(packet, clazz, Packet.class) PrettyPrinter.printObject(packet, clazz, MinecraftReflection.getPacketClass())
); );
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {

View File

@ -47,6 +47,7 @@ class CommandProtocol extends CommandBase {
return true; return true;
} }
@SuppressWarnings("deprecation")
public void checkVersion(final CommandSender sender) { public void checkVersion(final CommandSender sender) {
// Perform on an async thread // Perform on an async thread
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() { plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() {
@ -64,6 +65,7 @@ class CommandProtocol extends CommandBase {
updateFinished(); updateFinished();
} }
@SuppressWarnings("deprecation")
public void updateVersion(final CommandSender sender) { public void updateVersion(final CommandSender sender) {
// Perform on an async thread // Perform on an async thread
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() { plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() {

View File

@ -0,0 +1,161 @@
package com.comphenix.protocol;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.Server;
import com.google.common.base.Objects;
import com.google.common.collect.ComparisonChain;
/**
* Determine the current Minecraft version.
*
* @author Kristian
*/
class MinecraftVersion implements Comparable<MinecraftVersion> {
/**
* Regular expression used to parse version strings.
*/
private static final String VERSION_PATTERN = ".*\\(MC:\\s*((?:\\d+\\.)*\\d)\\s*\\)";
private final int major;
private final int minor;
private final int build;
/**
* Determine the current Minecraft version.
* @param server - the Bukkit server that will be used to examine the MC version.
*/
public MinecraftVersion(Server server) {
this(extractVersion(server.getVersion()));
}
/**
* Construct a version object from the format major.minor.build.
* @param versionOnly - the version in text form.
*/
public MinecraftVersion(String versionOnly) {
int[] numbers = parseVersion(versionOnly);
this.major = numbers[0];
this.minor = numbers[1];
this.build = numbers[2];
}
/**
* Construct a version object directly.
* @param major - major version number.
* @param minor - minor version number.
* @param build - build version number.
*/
public MinecraftVersion(int major, int minor, int build) {
this.major = major;
this.minor = minor;
this.build = build;
}
private int[] parseVersion(String version) {
String[] elements = version.split("\\.");
int[] numbers = new int[3];
// Make sure it's even a valid version
if (elements.length < 1)
throw new IllegalStateException("Corrupt MC version: " + version);
// The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively.
for (int i = 0; i < Math.min(numbers.length, elements.length); i++)
numbers[i] = Integer.parseInt(elements[i].trim());
return numbers;
}
/**
* Major version number
* @return Current major version number.
*/
public int getMajor() {
return major;
}
/**
* Minor version number
* @return Current minor version number.
*/
public int getMinor() {
return minor;
}
/**
* Build version number
* @return Current build version number.
*/
public int getBuild() {
return build;
}
/**
* Retrieve the version String (major.minor.build) only.
* @return A normal version string.
*/
public String getVersion() {
return String.format("%s.%s.%s", major, minor, build);
}
@Override
public int compareTo(MinecraftVersion o) {
if (o == null)
return 1;
return ComparisonChain.start().
compare(major, o.major).
compare(minor, o.minor).
compare(build, o.build).
result();
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj == this)
return true;
if (obj instanceof MinecraftVersion) {
MinecraftVersion other = (MinecraftVersion) obj;
return major == other.major &&
minor == other.minor &&
build == other.build;
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(major, minor, build);
}
@Override
public String toString() {
// Convert to a String that we can parse back again
return String.format("(MC: %s)", getVersion());
}
/**
* Extract the Minecraft version from CraftBukkit itself.
* @param server - the server object representing CraftBukkit.
* @return The underlying MC version.
* @throws IllegalStateException If we could not parse the version string.
*/
public static String extractVersion(String text) {
Pattern versionPattern = Pattern.compile(VERSION_PATTERN);
Matcher version = versionPattern.matcher(text);
if (version.matches() && version.group(1) != null) {
return version.group(1);
} else {
throw new IllegalStateException("Cannot parse version String '" + text + "'");
}
}
}

View File

@ -18,6 +18,8 @@ class ProtocolConfig {
private static final String METRICS_ENABLED = "metrics"; private static final String METRICS_ENABLED = "metrics";
private static final String IGNORE_VERSION_CHECK = "ignore version check";
private static final String BACKGROUND_COMPILER_ENABLED = "background compiler"; private static final String BACKGROUND_COMPILER_ENABLED = "background compiler";
private static final String UPDATER_NOTIFY = "notify"; private static final String UPDATER_NOTIFY = "notify";
@ -148,6 +150,26 @@ class ProtocolConfig {
public long getAutoLastTime() { public long getAutoLastTime() {
return updater.getLong(UPDATER_LAST_TIME, 0); return updater.getLong(UPDATER_LAST_TIME, 0);
} }
/**
* The version of Minecraft to ignore the built-in safety feature.
* @return The version to ignore ProtocolLib's satefy.
*/
public String getIgnoreVersionCheck() {
return global.getString(IGNORE_VERSION_CHECK, "");
}
/**
* Sets under which version of Minecraft the version safety feature will be ignored.
* <p>
* This is useful if a server operator has tested and verified that a version of ProtocolLib works,
* but doesn't want or can't upgrade to a newer version.
*
* @param ignoreVersion - the version of Minecraft where the satefy will be disabled.
*/
public void setIgnoreVersionCheck(String ignoreVersion) {
global.set(IGNORE_VERSION_CHECK, ignoreVersion);
}
/** /**
* Retrieve whether or not metrics is enabled. * Retrieve whether or not metrics is enabled.

View File

@ -19,6 +19,7 @@ package com.comphenix.protocol;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Handler; import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord; import java.util.logging.LogRecord;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -43,6 +44,15 @@ import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
* @author Kristian * @author Kristian
*/ */
public class ProtocolLibrary extends JavaPlugin { public class ProtocolLibrary extends JavaPlugin {
/**
* The minimum version ProtocolLib has been tested with.
*/
private static final String MINIMUM_MINECRAFT_VERSION = "1.0.0";
/**
* The maximum version ProtocolLib has been tested with,
*/
private static final String MAXIMUM_MINECRAFT_VERSION = "1.4.5";
/** /**
* The number of milliseconds per second. * The number of milliseconds per second.
@ -120,7 +130,7 @@ public class ProtocolLibrary extends JavaPlugin {
commandPacket = new CommandPacket(detailedReporter, this, logger, protocolManager); commandPacket = new CommandPacket(detailedReporter, this, logger, protocolManager);
// Send logging information to player listeners too // Send logging information to player listeners too
broadcastUsers(PERMISSION_INFO); setupBroadcastUsers(PERMISSION_INFO);
} catch (Throwable e) { } catch (Throwable e) {
detailedReporter.reportDetailed(this, "Cannot load ProtocolLib.", e, protocolManager); detailedReporter.reportDetailed(this, "Cannot load ProtocolLib.", e, protocolManager);
@ -142,7 +152,7 @@ public class ProtocolLibrary extends JavaPlugin {
} }
} }
private void broadcastUsers(final String permission) { private void setupBroadcastUsers(final String permission) {
// Guard against multiple calls // Guard against multiple calls
if (redirectHandler != null) if (redirectHandler != null)
return; return;
@ -151,7 +161,10 @@ public class ProtocolLibrary extends JavaPlugin {
redirectHandler = new Handler() { redirectHandler = new Handler() {
@Override @Override
public void publish(LogRecord record) { public void publish(LogRecord record) {
commandPacket.broadcastMessageSilently(record.getMessage(), permission); // Only display warnings and above
if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
commandPacket.broadcastMessageSilently(record.getMessage(), permission);
}
} }
@Override @Override
@ -188,13 +201,13 @@ public class ProtocolLibrary extends JavaPlugin {
logger.info("Structure compiler thread has been disabled."); logger.info("Structure compiler thread has been disabled.");
} }
// Handle unexpected Minecraft versions
verifyMinecraftVersion();
// Set up command handlers // Set up command handlers
registerCommand(CommandProtocol.NAME, commandProtocol); registerCommand(CommandProtocol.NAME, commandProtocol);
registerCommand(CommandPacket.NAME, commandPacket); registerCommand(CommandPacket.NAME, commandPacket);
// Notify server managers of incompatible plugins
checkForIncompatibility(manager);
// Player login and logout events // Player login and logout events
protocolManager.registerEvents(manager, this); protocolManager.registerEvents(manager, this);
@ -220,6 +233,26 @@ public class ProtocolLibrary extends JavaPlugin {
} }
} }
// Used to check Minecraft version
private void verifyMinecraftVersion() {
try {
MinecraftVersion minimum = new MinecraftVersion(MINIMUM_MINECRAFT_VERSION);
MinecraftVersion maximum = new MinecraftVersion(MAXIMUM_MINECRAFT_VERSION);
MinecraftVersion current = new MinecraftVersion(getServer());
// Skip certain versions
if (!config.getIgnoreVersionCheck().equals(current.getVersion())) {
// We'll just warn the user for now
if (current.compareTo(minimum) < 0)
logger.warning("Version " + current + " is lower than the minimum " + minimum);
if (current.compareTo(maximum) > 0)
logger.warning("Version " + current + " has not yet been tested! Proceed with caution.");
}
} catch (Exception e) {
reporter.reportWarning(this, "Unable to retrieve current Minecraft version.", e);
}
}
private void registerCommand(String name, CommandExecutor executor) { private void registerCommand(String name, CommandExecutor executor) {
try { try {
if (executor == null) if (executor == null)
@ -296,18 +329,6 @@ public class ProtocolLibrary extends JavaPlugin {
} }
} }
private void checkForIncompatibility(PluginManager manager) {
// Plugin authors: Notify me to remove these
String[] incompatiblePlugins = {};
for (String plugin : incompatiblePlugins) {
if (manager.getPlugin(plugin) != null) {
// Check for versions, ect.
reporter.reportWarning(this, "Detected incompatible plugin: " + plugin);
}
}
}
@Override @Override
public void onDisable() { public void onDisable() {
// Disable compiler // Disable compiler

View File

@ -242,7 +242,7 @@ public class AsyncListenerHandler {
final AsyncRunnable listenerLoop = getListenerLoop(); final AsyncRunnable listenerLoop = getListenerLoop();
filterManager.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), new Runnable() { scheduleAsync(new Runnable() {
@Override @Override
public void run() { public void run() {
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
@ -290,7 +290,7 @@ public class AsyncListenerHandler {
final AsyncRunnable listenerLoop = getListenerLoop(); final AsyncRunnable listenerLoop = getListenerLoop();
final Function<AsyncRunnable, Void> delegateCopy = executor; final Function<AsyncRunnable, Void> delegateCopy = executor;
filterManager.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), new Runnable() { scheduleAsync(new Runnable() {
@Override @Override
public void run() { public void run() {
delegateCopy.apply(listenerLoop); delegateCopy.apply(listenerLoop);
@ -298,6 +298,11 @@ public class AsyncListenerHandler {
}); });
} }
@SuppressWarnings("deprecation")
private void scheduleAsync(Runnable runnable) {
filterManager.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), runnable);
}
/** /**
* Create a friendly thread name using the following convention: * Create a friendly thread name using the following convention:
* <p><code> * <p><code>

View File

@ -25,13 +25,12 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import net.minecraft.server.Packet;
import com.comphenix.protocol.PacketStream; import com.comphenix.protocol.PacketStream;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.PrioritizedListener; import com.comphenix.protocol.injector.PrioritizedListener;
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.google.common.primitives.Longs; import com.google.common.primitives.Longs;
/** /**
@ -403,10 +402,10 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
if (isMinecraftAsync == null && !alwaysSync) { if (isMinecraftAsync == null && !alwaysSync) {
try { try {
isMinecraftAsync = FuzzyReflection.fromClass(Packet.class).getMethodByName("a_.*"); isMinecraftAsync = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethodByName("a_.*");
} catch (RuntimeException e) { } catch (RuntimeException e) {
// This will occur in 1.2.5 (or possibly in later versions) // This will occur in 1.2.5 (or possibly in later versions)
List<Method> methods = FuzzyReflection.fromClass(Packet.class). List<Method> methods = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
getMethodListByParameters(boolean.class, new Class[] {}); getMethodListByParameters(boolean.class, new Class[] {});
// Try to look for boolean methods // Try to look for boolean methods

View File

@ -95,6 +95,21 @@ public class DetailedErrorReporter implements ErrorReporter {
this.logger = logger; this.logger = logger;
} }
@Override
public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) {
reportMinimal(sender, methodName, error);
// Print parameters, if they are given
if (parameters != null && parameters.length > 0) {
logger.log(Level.SEVERE, " Parameters:");
// Print each parameter
for (Object parameter : parameters) {
logger.log(Level.SEVERE, " " + getStringDescription(parameter));
}
}
}
@Override @Override
public void reportMinimal(Plugin sender, String methodName, Throwable error) { public void reportMinimal(Plugin sender, String methodName, Throwable error) {
logger.log(Level.SEVERE, "[" + PLUGIN_NAME + "] Unhandled exception occured in " + methodName + " for " + logger.log(Level.SEVERE, "[" + PLUGIN_NAME + "] Unhandled exception occured in " + methodName + " for " +

View File

@ -12,6 +12,15 @@ public interface ErrorReporter {
*/ */
public abstract void reportMinimal(Plugin sender, String methodName, Throwable error); public abstract void reportMinimal(Plugin sender, String methodName, Throwable error);
/**
* Prints a small minimal error report about an exception from another plugin.
* @param sender - the other plugin.
* @param methodName - name of the caller method.
* @param error - the exception itself.
* @param parameters - any relevant parameters to print.
*/
public abstract void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters);
/** /**
* Prints a warning message from the current plugin. * Prints a warning message from the current plugin.
* @param sender - the object containing the caller method. * @param sender - the object containing the caller method.

View File

@ -29,6 +29,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentMap;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.WorldType; import org.bukkit.WorldType;
@ -39,12 +40,12 @@ import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedWatchableObject; import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.google.common.collect.Maps;
import net.minecraft.server.Packet;
/** /**
* Represents a Minecraft packet indirectly. * Represents a Minecraft packet indirectly.
@ -59,15 +60,15 @@ public class PacketContainer implements Serializable {
private static final long serialVersionUID = 2074805748222377230L; private static final long serialVersionUID = 2074805748222377230L;
protected int id; protected int id;
protected transient Packet handle; protected transient Object handle;
// Current structure modifier // Current structure modifier
protected transient StructureModifier<Object> structureModifier; protected transient StructureModifier<Object> structureModifier;
// Support for serialization // Support for serialization
private static Method writeMethod; private static ConcurrentMap<Class<?>, Method> writeMethods = Maps.newConcurrentMap();
private static Method readMethod; private static ConcurrentMap<Class<?>, Method> readMethods = Maps.newConcurrentMap();
/** /**
* Creates a packet container for a new packet. * Creates a packet container for a new packet.
* @param id - ID of the packet to create. * @param id - ID of the packet to create.
@ -81,7 +82,7 @@ public class PacketContainer implements Serializable {
* @param id - ID of the given packet. * @param id - ID of the given packet.
* @param handle - contained packet. * @param handle - contained packet.
*/ */
public PacketContainer(int id, Packet handle) { public PacketContainer(int id, Object handle) {
this(id, handle, StructureCache.getStructure(id).withTarget(handle)); this(id, handle, StructureCache.getStructure(id).withTarget(handle));
} }
@ -91,7 +92,7 @@ public class PacketContainer implements Serializable {
* @param handle - contained packet. * @param handle - contained packet.
* @param structure - structure modifier. * @param structure - structure modifier.
*/ */
public PacketContainer(int id, Packet handle, StructureModifier<Object> structure) { public PacketContainer(int id, Object handle, StructureModifier<Object> structure) {
if (handle == null) if (handle == null)
throw new IllegalArgumentException("handle cannot be null."); throw new IllegalArgumentException("handle cannot be null.");
@ -100,11 +101,17 @@ public class PacketContainer implements Serializable {
this.structureModifier = structure; this.structureModifier = structure;
} }
/**
* For serialization.
*/
protected PacketContainer() {
}
/** /**
* Retrieves the underlying Minecraft packet. * Retrieves the underlying Minecraft packet.
* @return Underlying Minecraft packet. * @return Underlying Minecraft packet.
*/ */
public Packet getHandle() { public Object getHandle() {
return handle; return handle;
} }
@ -214,7 +221,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<ItemStack> getItemModifier() { public StructureModifier<ItemStack> getItemModifier() {
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.<ItemStack>withType( return structureModifier.<ItemStack>withType(
net.minecraft.server.ItemStack.class, BukkitConverters.getItemStackConverter()); MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter());
} }
/** /**
@ -230,23 +237,23 @@ public class PacketContainer implements Serializable {
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.<ItemStack[]>withType( return structureModifier.<ItemStack[]>withType(
net.minecraft.server.ItemStack[].class, MinecraftReflection.getItemStackArrayClass(),
BukkitConverters.getIgnoreNull(new EquivalentConverter<ItemStack[]>() { BukkitConverters.getIgnoreNull(new EquivalentConverter<ItemStack[]>() {
public Object getGeneric(Class<?>genericType, ItemStack[] specific) { public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
net.minecraft.server.ItemStack[] result = new net.minecraft.server.ItemStack[specific.length]; Object[] result = new Object[specific.length];
// Unwrap every item // Unwrap every item
for (int i = 0; i < result.length; i++) { for (int i = 0; i < result.length; i++) {
result[i] = (net.minecraft.server.ItemStack) stackConverter.getGeneric( result[i] = stackConverter.getGeneric(
net.minecraft.server.ItemStack.class, specific[i]); MinecraftReflection.getItemStackClass(), specific[i]);
} }
return result; return result;
} }
@Override @Override
public ItemStack[] getSpecific(Object generic) { public ItemStack[] getSpecific(Object generic) {
net.minecraft.server.ItemStack[] input = (net.minecraft.server.ItemStack[]) generic; Object[] input = (Object[]) generic;
ItemStack[] result = new ItemStack[input.length]; ItemStack[] result = new ItemStack[input.length];
// Add the wrapper // Add the wrapper
@ -273,7 +280,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<WorldType> getWorldTypeModifier() { public StructureModifier<WorldType> getWorldTypeModifier() {
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.<WorldType>withType( return structureModifier.<WorldType>withType(
net.minecraft.server.WorldType.class, MinecraftReflection.getWorldTypeClass(),
BukkitConverters.getWorldTypeConverter()); BukkitConverters.getWorldTypeConverter());
} }
@ -284,7 +291,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<WrappedDataWatcher> getDataWatcherModifier() { public StructureModifier<WrappedDataWatcher> getDataWatcherModifier() {
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.<WrappedDataWatcher>withType( return structureModifier.<WrappedDataWatcher>withType(
net.minecraft.server.DataWatcher.class, MinecraftReflection.getDataWatcherClass(),
BukkitConverters.getDataWatcherConverter()); BukkitConverters.getDataWatcherConverter());
} }
@ -311,7 +318,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<ChunkPosition> getPositionModifier() { public StructureModifier<ChunkPosition> getPositionModifier() {
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.withType( return structureModifier.withType(
net.minecraft.server.ChunkPosition.class, MinecraftReflection.getChunkPositionClass(),
ChunkPosition.getConverter()); ChunkPosition.getConverter());
} }
@ -327,7 +334,7 @@ public class PacketContainer implements Serializable {
return structureModifier.withType( return structureModifier.withType(
Collection.class, Collection.class,
BukkitConverters.getListConverter( BukkitConverters.getListConverter(
net.minecraft.server.ChunkPosition.class, MinecraftReflection.getChunkPositionClass(),
ChunkPosition.getConverter()) ChunkPosition.getConverter())
); );
} }
@ -344,7 +351,7 @@ public class PacketContainer implements Serializable {
return structureModifier.withType( return structureModifier.withType(
Collection.class, Collection.class,
BukkitConverters.getListConverter( BukkitConverters.getListConverter(
net.minecraft.server.WatchableObject.class, MinecraftReflection.getWatchableObjectClass(),
BukkitConverters.getWatchableObjectConverter()) BukkitConverters.getWatchableObjectConverter())
); );
} }
@ -399,14 +406,12 @@ public class PacketContainer implements Serializable {
// We'll take care of NULL packets as well // We'll take care of NULL packets as well
output.writeBoolean(handle != null); output.writeBoolean(handle != null);
// Retrieve the write method by reflection
if (writeMethod == null)
writeMethod = FuzzyReflection.fromObject(handle).getMethodByParameters("write", DataOutputStream.class);
try { try {
// Call the write-method // Call the write-method
writeMethod.invoke(handle, new DataOutputStream(output)); getMethodLazily(writeMethods, handle.getClass(), "write", DataOutputStream.class).
invoke(handle, new DataOutputStream(output));
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw new IOException("Minecraft packet doesn't support DataOutputStream", e); throw new IOException("Minecraft packet doesn't support DataOutputStream", e);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
@ -429,13 +434,11 @@ public class PacketContainer implements Serializable {
// Create a default instance of the packet // Create a default instance of the packet
handle = StructureCache.newPacket(id); handle = StructureCache.newPacket(id);
// Retrieve the read method by reflection
if (readMethod == null)
readMethod = FuzzyReflection.fromObject(handle).getMethodByParameters("read", DataInputStream.class);
// Call the read method // Call the read method
try { try {
readMethod.invoke(handle, new DataInputStream(input)); getMethodLazily(readMethods, handle.getClass(), "read", DataInputStream.class).
invoke(handle, new DataInputStream(input));
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw new IOException("Minecraft packet doesn't support DataInputStream", e); throw new IOException("Minecraft packet doesn't support DataInputStream", e);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
@ -448,4 +451,30 @@ public class PacketContainer implements Serializable {
structureModifier = structureModifier.withTarget(handle); structureModifier = structureModifier.withTarget(handle);
} }
} }
/**
* Retrieve the cached method concurrently.
* @param lookup - a lazy lookup cache.
* @param handleClass - class type of the current packet.
* @param methodName - name of method to retrieve.
* @param parameterClass - the one parameter type in the method.
* @return Reflected method.
*/
private Method getMethodLazily(ConcurrentMap<Class<?>, Method> lookup,
Class<?> handleClass, String methodName, Class<?> parameterClass) {
Method method = lookup.get(handleClass);
// Atomic operation
if (method == null) {
Method initialized = FuzzyReflection.fromClass(handleClass).getMethodByParameters(methodName, parameterClass);
method = lookup.putIfAbsent(handleClass, initialized);
// Use our version if we succeeded
if (method == null) {
method = initialized;
}
}
return method;
}
} }

View File

@ -28,17 +28,14 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.EntityTrackerEntry;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.comphenix.protocol.reflect.FieldAccessException; 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.utility.MinecraftReflection;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
/** /**
@ -51,10 +48,11 @@ class EntityUtilities {
private static Field entityTrackerField; private static Field entityTrackerField;
private static Field trackedEntitiesField; private static Field trackedEntitiesField;
private static Field trackedPlayersField; private static Field trackedPlayersField;
private static Field trackerField;
private static Method hashGetMethod; private static Method hashGetMethod;
private static Method scanPlayersMethod; private static Method scanPlayersMethod;
/* /*
* While this function may look pretty bad, it's essentially just a reflection-warped * While this function may look pretty bad, it's essentially just a reflection-warped
* version of the following: * version of the following:
@ -142,9 +140,8 @@ class EntityUtilities {
// Wrap every player - we also ensure that the underlying tracker list is immutable // Wrap every player - we also ensure that the underlying tracker list is immutable
for (Object tracker : trackedPlayers) { for (Object tracker : trackedPlayers) {
if (tracker instanceof EntityPlayer) { if (MinecraftReflection.isMinecraftPlayer(tracker)) {
EntityPlayer nmsPlayer = (EntityPlayer) tracker; result.add((Player) MinecraftReflection.getBukkitEntity(tracker));
result.add(nmsPlayer.getBukkitEntity());
} }
} }
return result; return result;
@ -164,7 +161,8 @@ class EntityUtilities {
* @throws FieldAccessException * @throws FieldAccessException
*/ */
private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Object worldServer = ((CraftWorld) world).getHandle(); BukkitUnwrapper unwrapper = new BukkitUnwrapper();
Object worldServer = unwrapper.unwrapItem(world);
// We have to rely on the class naming here. // We have to rely on the class naming here.
if (entityTrackerField == null) if (entityTrackerField == null)
@ -193,7 +191,7 @@ class EntityUtilities {
// The Minecraft field that's NOT filled in by the constructor // The Minecraft field that's NOT filled in by the constructor
trackedEntitiesField = FuzzyReflection.fromObject(tracker, true). trackedEntitiesField = FuzzyReflection.fromObject(tracker, true).
getFieldByType(FuzzyReflection.MINECRAFT_OBJECT, ignoredTypes); getFieldByType(MinecraftReflection.MINECRAFT_OBJECT, ignoredTypes);
} }
// Read the entity hashmap // Read the entity hashmap
@ -247,17 +245,24 @@ class EntityUtilities {
*/ */
public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException { public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException {
try { try {
EntityTrackerEntry trackerEntry = (EntityTrackerEntry) getEntityTrackerEntry(world, entityID); Object trackerEntry = getEntityTrackerEntry(world, entityID);
Object tracker = null;
// Handle NULL cases // Handle NULL cases
if (trackerEntry != null && trackerEntry.tracker != null) { if (trackerEntry != null) {
return trackerEntry.tracker.getBukkitEntity(); if (trackerField == null)
} else { trackerField = trackerEntry.getClass().getField("tracker");
return null; tracker = FieldUtils.readField(trackerField, trackerEntry, true);
} }
// If the tracker is NULL, we'll just assume this entity doesn't exist
if (tracker != null)
return (Entity) MinecraftReflection.getBukkitEntity(tracker);
else
return null;
} catch (Exception e) { } catch (Exception e) {
throw new FieldAccessException("Cannot find entity from ID.", e); throw new FieldAccessException("Cannot find entity from ID " + entityID + ".", e);
} }
} }

View File

@ -17,8 +17,6 @@
package com.comphenix.protocol.injector; package com.comphenix.protocol.injector;
import net.minecraft.server.Packet;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
/** /**
@ -45,7 +43,7 @@ public interface ListenerInvoker {
* @param packet - the packet. * @param packet - the packet.
* @return The packet ID. * @return The packet ID.
*/ */
public abstract int getPacketID(Packet packet); public abstract int getPacketID(Object packet);
/** /**
* Associate a given class with the given packet ID. Internal method. * Associate a given class with the given packet ID. Internal method.

View File

@ -23,11 +23,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import net.minecraft.server.Packet; import net.sf.cglib.proxy.Factory;
import com.comphenix.protocol.reflect.FieldAccessException; 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.utility.MinecraftReflection;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
@ -77,7 +78,7 @@ class MinecraftRegistry {
*/ */
private static FuzzyReflection getPacketRegistry() { private static FuzzyReflection getPacketRegistry() {
if (packetRegistry == null) if (packetRegistry == null)
packetRegistry = FuzzyReflection.fromClass(Packet.class, true); packetRegistry = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true);
return packetRegistry; return packetRegistry;
} }
@ -166,7 +167,7 @@ class MinecraftRegistry {
// Optimized lookup // Optimized lookup
if (lookup.containsKey(packetID)) { if (lookup.containsKey(packetID)) {
return lookup.get(packetID); return removeEnhancer(lookup.get(packetID), forceVanilla);
} }
// Will most likely not be used // Will most likely not be used
@ -174,10 +175,27 @@ class MinecraftRegistry {
if (Objects.equal(entry.getValue(), packetID)) { if (Objects.equal(entry.getValue(), packetID)) {
// Attempt to get the vanilla class here too // Attempt to get the vanilla class here too
if (!forceVanilla || entry.getKey().getName().startsWith("net.minecraft.server")) if (!forceVanilla || entry.getKey().getName().startsWith("net.minecraft.server"))
return entry.getKey(); return removeEnhancer(entry.getKey(), forceVanilla);
} }
} }
throw new IllegalArgumentException("The packet ID " + packetID + " is not registered."); throw new IllegalArgumentException("The packet ID " + packetID + " is not registered.");
} }
/**
* Find the first superclass that is not a CBLib proxy object.
* @param clazz - the class whose hierachy we're going to search through.
* @param remove - whether or not to skip enhanced (proxy) classes.
* @return If remove is TRUE, the first superclass that is not a proxy.
*/
private static Class removeEnhancer(Class clazz, boolean remove) {
if (remove) {
// Get the underlying vanilla class
while (Factory.class.isAssignableFrom(clazz) && !clazz.equals(Object.class)) {
clazz = clazz.getSuperclass();
}
}
return clazz;
}
} }

View File

@ -21,8 +21,6 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.List; import java.util.List;
import net.minecraft.server.Packet;
import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -157,7 +155,7 @@ public class PacketConstructor {
} }
} }
Packet nmsPacket = (Packet) constructorMethod.newInstance(values); Object nmsPacket = constructorMethod.newInstance(values);
return new PacketContainer(packetID, nmsPacket); return new PacketContainer(packetID, nmsPacket);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {

View File

@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import net.sf.cglib.proxy.MethodProxy;
@ -55,6 +54,7 @@ import com.comphenix.protocol.events.*;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler; import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
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.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;
@ -508,7 +508,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
if (packetCreation.compareAndSet(false, true)) if (packetCreation.compareAndSet(false, true))
incrementPhases(GamePhase.PLAYING); incrementPhases(GamePhase.PLAYING);
Packet mcPacket = packet.getHandle(); Object mcPacket = packet.getHandle();
// Make sure the packet isn't cancelled // Make sure the packet isn't cancelled
packetInjector.undoCancel(packet.getID(), mcPacket); packetInjector.undoCancel(packet.getID(), mcPacket);
@ -686,9 +686,11 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
} }
@Override @Override
public int getPacketID(Packet packet) { public int getPacketID(Object packet) {
if (packet == null) if (packet == null)
throw new IllegalArgumentException("Packet cannot be NULL."); throw new IllegalArgumentException("Packet cannot be NULL.");
if (!MinecraftReflection.isPacketClass(packet))
throw new IllegalArgumentException("The given object " + packet + " is not a packet.");
return MinecraftRegistry.getPacketToID().get(packet.getClass()); return MinecraftRegistry.getPacketToID().get(packet.getClass());
} }

View File

@ -27,7 +27,6 @@ import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.Enhancer;
@ -37,6 +36,7 @@ import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler; import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
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.utility.MinecraftReflection;
/** /**
* This class is responsible for adding or removing proxy objects that intercepts recieved packets. * This class is responsible for adding or removing proxy objects that intercepts recieved packets.
@ -80,7 +80,7 @@ class PacketInjector {
* @param id - the id of the packet. * @param id - the id of the packet.
* @param packet - packet to uncancel. * @param packet - packet to uncancel.
*/ */
public void undoCancel(Integer id, Packet packet) { public void undoCancel(Integer id, Object packet) {
ReadPacketModifier modifier = readModifier.get(id); ReadPacketModifier modifier = readModifier.get(id);
// See if this packet has been cancelled before // See if this packet has been cancelled before
@ -92,7 +92,8 @@ class PacketInjector {
private void initialize() throws IllegalAccessException { private void initialize() throws IllegalAccessException {
if (intHashMap == null) { if (intHashMap == null) {
// We're looking for the first static field with a Minecraft-object. This should be a IntHashMap. // We're looking for the first static field with a Minecraft-object. This should be a IntHashMap.
Field intHashMapField = FuzzyReflection.fromClass(Packet.class, true).getFieldByType(FuzzyReflection.MINECRAFT_OBJECT); Field intHashMapField = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).
getFieldByType(MinecraftReflection.MINECRAFT_OBJECT);
try { try {
intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true); intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true);

View File

@ -29,7 +29,6 @@ import com.comphenix.protocol.error.ErrorReporter;
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 net.minecraft.server.Packet;
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import net.sf.cglib.proxy.MethodProxy;
@ -61,7 +60,7 @@ class ReadPacketModifier implements MethodInterceptor {
* Remove any packet overrides. * Remove any packet overrides.
* @param packet - the packet to rever * @param packet - the packet to rever
*/ */
public void removeOverride(Packet packet) { public void removeOverride(Object packet) {
override.remove(packet); override.remove(packet);
} }
@ -70,7 +69,7 @@ class ReadPacketModifier implements MethodInterceptor {
* @param packet - the given packet. * @param packet - the given packet.
* @return Overriden object. * @return Overriden object.
*/ */
public Object getOverride(Packet packet) { public Object getOverride(Object packet) {
return override.get(packet); return override.get(packet);
} }
@ -79,7 +78,7 @@ class ReadPacketModifier implements MethodInterceptor {
* @param packet - the packet to check. * @param packet - the packet to check.
* @return TRUE if it has been cancelled, FALSE otherwise. * @return TRUE if it has been cancelled, FALSE otherwise.
*/ */
public boolean hasCancelled(Packet packet) { public boolean hasCancelled(Object packet) {
return getOverride(packet) == CANCEL_MARKER; return getOverride(packet) == CANCEL_MARKER;
} }
@ -121,12 +120,12 @@ class ReadPacketModifier implements MethodInterceptor {
DataInputStream input = (DataInputStream) args[0]; DataInputStream input = (DataInputStream) args[0];
// Let the people know // Let the people know
PacketContainer container = new PacketContainer(packetID, (Packet) thisObj); PacketContainer container = new PacketContainer(packetID, thisObj);
PacketEvent event = packetInjector.packetRecieved(container, input); PacketEvent event = packetInjector.packetRecieved(container, input);
// Handle override // Handle override
if (event != null) { if (event != null) {
Packet result = event.getPacket().getHandle(); Object result = event.getPacket().getHandle();
if (event.isCancelled()) { if (event.isCancelled()) {
override.put(thisObj, CANCEL_MARKER); override.put(thisObj, CANCEL_MARKER);

View File

@ -48,7 +48,8 @@ public final class SortedPacketListenerList extends AbstractConcurrentListenerMu
element.getListener().onPacketReceiving(event); element.getListener().onPacketReceiving(event);
} catch (Throwable e) { } catch (Throwable e) {
// Minecraft doesn't want your Exception. // Minecraft doesn't want your Exception.
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketReceiving()", e); reporter.reportMinimal(element.getListener().getPlugin(), "onPacketReceiving(PacketEvent)", e,
event.getPacket().getHandle());
} }
} }
} }
@ -69,7 +70,8 @@ public final class SortedPacketListenerList extends AbstractConcurrentListenerMu
element.getListener().onPacketSending(event); element.getListener().onPacketSending(event);
} catch (Throwable e) { } catch (Throwable e) {
// Minecraft doesn't want your Exception. // Minecraft doesn't want your Exception.
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketSending()", e); reporter.reportMinimal(element.getListener().getPlugin(), "onPacketSending(PacketEvent)", e,
event.getPacket().getHandle());
} }
} }
} }

View File

@ -22,12 +22,11 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import net.minecraft.server.Packet;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler; import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.reflect.compiler.CompileListener; import com.comphenix.protocol.reflect.compiler.CompileListener;
import com.comphenix.protocol.reflect.compiler.CompiledStructureModifier; import com.comphenix.protocol.reflect.compiler.CompiledStructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
/** /**
* Caches structure modifiers. * Caches structure modifiers.
@ -45,9 +44,9 @@ public class StructureCache {
* @param id - packet ID. * @param id - packet ID.
* @return Created packet. * @return Created packet.
*/ */
public static Packet newPacket(int id) { public static Object newPacket(int id) {
try { try {
return (Packet) MinecraftRegistry.getPacketClassFromID(id, true).newInstance(); return MinecraftRegistry.getPacketClassFromID(id, true).newInstance();
} catch (InstantiationException e) { } catch (InstantiationException e) {
return null; return null;
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
@ -79,7 +78,7 @@ public class StructureCache {
if (result == null) { if (result == null) {
// Use the vanilla class definition // Use the vanilla class definition
final StructureModifier<Object> value = new StructureModifier<Object>( final StructureModifier<Object> value = new StructureModifier<Object>(
MinecraftRegistry.getPacketClassFromID(id, true), Packet.class, true); MinecraftRegistry.getPacketClassFromID(id, true), MinecraftReflection.getPacketClass(), true);
result = structureModifiers.putIfAbsent(id, value); result = structureModifiers.putIfAbsent(id, value);

View File

@ -25,7 +25,7 @@ import java.util.Set;
import com.comphenix.protocol.injector.ListenerInvoker; import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket; import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket;
import net.minecraft.server.Packet; import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import net.sf.cglib.proxy.MethodProxy;
@ -35,7 +35,7 @@ import net.sf.cglib.proxy.MethodProxy;
* *
* @author Kristian * @author Kristian
*/ */
class InjectedArrayList extends ArrayList<Packet> { class InjectedArrayList extends ArrayList<Object> {
/** /**
* Silly Eclipse. * Silly Eclipse.
@ -43,19 +43,22 @@ class InjectedArrayList extends ArrayList<Packet> {
private static final long serialVersionUID = -1173865905404280990L; private static final long serialVersionUID = -1173865905404280990L;
private transient PlayerInjector injector; private transient PlayerInjector injector;
private transient Set<Packet> ignoredPackets; private transient Set<Object> ignoredPackets;
private transient ClassLoader classLoader; private transient ClassLoader classLoader;
public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set<Packet> ignoredPackets) { private transient InvertedIntegerCallback callback;
public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set<Object> ignoredPackets) {
this.classLoader = classLoader; this.classLoader = classLoader;
this.injector = injector; this.injector = injector;
this.ignoredPackets = ignoredPackets; this.ignoredPackets = ignoredPackets;
this.callback = new InvertedIntegerCallback();
} }
@Override @Override
public boolean add(Packet packet) { public boolean add(Object packet) {
Packet result = null; Object result = null;
// Check for fake packets and ignored packets // Check for fake packets and ignored packets
if (packet instanceof FakePacket) { if (packet instanceof FakePacket) {
@ -90,11 +93,13 @@ class InjectedArrayList extends ArrayList<Packet> {
* @param source - packet to invert. * @param source - packet to invert.
* @return The inverted packet. * @return The inverted packet.
*/ */
Packet createNegativePacket(Packet source) { Object createNegativePacket(Object source) {
ListenerInvoker invoker = injector.getInvoker(); ListenerInvoker invoker = injector.getInvoker();
int packetID = invoker.getPacketID(source); int packetID = invoker.getPacketID(source);
Class<?> type = invoker.getPacketClassFromID(packetID, true); Class<?> type = invoker.getPacketClassFromID(packetID, true);
System.out.println(type.getName());
// We want to subtract the byte amount that were added to the running // We want to subtract the byte amount that were added to the running
// total of outstanding packets. Otherwise, cancelling too many packets // total of outstanding packets. Otherwise, cancelling too many packets
@ -122,15 +127,20 @@ class InjectedArrayList extends ArrayList<Packet> {
ex.setCallbackType(InvertedIntegerCallback.class); ex.setCallbackType(InvertedIntegerCallback.class);
Class<?> proxyClass = ex.createClass(); Class<?> proxyClass = ex.createClass();
Enhancer.registerCallbacks(proxyClass, new Callback[] { callback });
// Temporarily associate the fake packet class
invoker.registerPacketClass(proxyClass, packetID);
Packet fake = (Packet) Enhancer.create(proxyClass, new InvertedIntegerCallback());
// Remove this association try {
invoker.unregisterPacketClass(proxyClass); // Temporarily associate the fake packet class
return fake; invoker.registerPacketClass(proxyClass, packetID);
return proxyClass.newInstance();
} catch (Exception e) {
// Don't pollute the throws tree
throw new RuntimeException("Cannot create fake class.", e);
} finally {
// Remove this association
invoker.unregisterPacketClass(proxyClass);
}
} }
/** /**

View File

@ -22,7 +22,6 @@ import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.minecraft.server.NetLoginHandler;
import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.Factory;
import org.bukkit.Server; import org.bukkit.Server;
@ -32,6 +31,7 @@ import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectCloner; import com.comphenix.protocol.reflect.ObjectCloner;
import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.utility.MinecraftReflection;
/** /**
* Used to ensure that the 1.3 server is referencing the correct server handler. * Used to ensure that the 1.3 server is referencing the correct server handler.
@ -229,7 +229,7 @@ class InjectedServerConnection {
@Override @Override
protected void onInserting(Object inserting) { protected void onInserting(Object inserting) {
// Ready for some login handler injection? // Ready for some login handler injection?
if (inserting instanceof NetLoginHandler) { if (MinecraftReflection.isLoginHandler(inserting)) {
Object replaced = netLoginInjector.onNetLoginCreated(inserting); Object replaced = netLoginInjector.onNetLoginCreated(inserting);
// Only replace if it has changed // Only replace if it has changed
@ -241,7 +241,7 @@ class InjectedServerConnection {
@Override @Override
protected void onRemoved(Object removing) { protected void onRemoved(Object removing) {
// Clean up? // Clean up?
if (removing instanceof NetLoginHandler) { if (MinecraftReflection.isLoginHandler(removing)) {
netLoginInjector.cleanup(removing); netLoginInjector.cleanup(removing);
} }
} }

View File

@ -40,8 +40,6 @@ import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.VolatileField;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import net.minecraft.server.Packet;
/** /**
* Injection hook that overrides the packet queue lists in NetworkHandler. * Injection hook that overrides the packet queue lists in NetworkHandler.
* *
@ -58,7 +56,7 @@ class NetworkFieldInjector extends PlayerInjector {
} }
// Packets to ignore // Packets to ignore
private Set<Packet> ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap<Packet, Boolean>()); private Set<Object> ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap<Object, Boolean>());
// Overridden fields // Overridden fields
private List<VolatileField> overridenLists = new ArrayList<VolatileField>(); private List<VolatileField> overridenLists = new ArrayList<VolatileField>();
@ -99,7 +97,7 @@ class NetworkFieldInjector extends PlayerInjector {
} }
@Override @Override
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
if (networkManager != null) { if (networkManager != null) {
try { try {
@ -147,14 +145,14 @@ class NetworkFieldInjector extends PlayerInjector {
VolatileField overwriter = new VolatileField(field, networkManager, true); VolatileField overwriter = new VolatileField(field, networkManager, true);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<Packet> minecraftList = (List<Packet>) overwriter.getOldValue(); List<Object> minecraftList = (List<Object>) overwriter.getOldValue();
synchronized(syncObject) { synchronized(syncObject) {
// The list we'll be inserting // The list we'll be inserting
List<Packet> hackedList = new InjectedArrayList(classLoader, this, ignoredPackets); List<Object> hackedList = new InjectedArrayList(classLoader, this, ignoredPackets);
// Add every previously stored packet // Add every previously stored packet
for (Packet packet : minecraftList) { for (Object packet : minecraftList) {
hackedList.add(packet); hackedList.add(packet);
} }
@ -172,8 +170,8 @@ class NetworkFieldInjector extends PlayerInjector {
protected void cleanHook() { protected void cleanHook() {
// Clean up // Clean up
for (VolatileField overriden : overridenLists) { for (VolatileField overriden : overridenLists) {
List<Packet> minecraftList = (List<Packet>) overriden.getOldValue(); List<Object> minecraftList = (List<Object>) overriden.getOldValue();
List<Packet> hacketList = (List<Packet>) overriden.getValue(); List<Object> hacketList = (List<Object>) overriden.getValue();
if (minecraftList == hacketList) { if (minecraftList == hacketList) {
return; return;
@ -183,7 +181,7 @@ class NetworkFieldInjector extends PlayerInjector {
synchronized(syncObject) { synchronized(syncObject) {
try { try {
// Copy over current packets // Copy over current packets
for (Packet packet : (List<Packet>) overriden.getValue()) { for (Object packet : (List<Object>) overriden.getValue()) {
minecraftList.add(packet); minecraftList.add(packet);
} }
} finally { } finally {

View File

@ -19,7 +19,6 @@ package com.comphenix.protocol.injector.player;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter; import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.Enhancer;
@ -67,7 +66,7 @@ class NetworkObjectInjector extends PlayerInjector {
} }
@Override @Override
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
Object networkDelegate = filtered ? networkManagerRef.getValue() : networkManagerRef.getOldValue(); Object networkDelegate = filtered ? networkManagerRef.getValue() : networkManagerRef.getOldValue();
if (networkDelegate != null) { if (networkDelegate != null) {
@ -114,7 +113,7 @@ class NetworkObjectInjector extends PlayerInjector {
Callback queueFilter = new MethodInterceptor() { Callback queueFilter = new MethodInterceptor() {
@Override @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Packet packet = (Packet) args[0]; Object packet = args[0];
if (packet != null) { if (packet != null) {
packet = handlePacketSending(packet); packet = handlePacketSending(packet);

View File

@ -21,7 +21,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter; import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.Enhancer;
@ -43,6 +42,7 @@ import com.comphenix.protocol.reflect.ObjectCloner;
import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.ExistingGenerator; import com.comphenix.protocol.reflect.instances.ExistingGenerator;
import com.comphenix.protocol.utility.MinecraftReflection;
/** /**
* Represents a player hook into the NetServerHandler class. * Represents a player hook into the NetServerHandler class.
@ -94,7 +94,7 @@ public class NetworkServerInjector extends PlayerInjector {
} }
@Override @Override
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
Object serverDeleage = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue(); Object serverDeleage = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue();
if (serverDeleage != null) { if (serverDeleage != null) {
@ -152,8 +152,7 @@ public class NetworkServerInjector extends PlayerInjector {
Callback sendPacketCallback = new MethodInterceptor() { Callback sendPacketCallback = new MethodInterceptor() {
@Override @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object packet = args[0];
Packet packet = (Packet) args[0];
if (packet != null) { if (packet != null) {
packet = handlePacketSending(packet); packet = handlePacketSending(packet);
@ -237,7 +236,7 @@ public class NetworkServerInjector extends PlayerInjector {
} }
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) { private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
if (clazz.getName().startsWith("net.minecraft.server.")) if (clazz.getName().startsWith(MinecraftReflection.getMinecraftPackage()))
return clazz; return clazz;
else if (clazz.equals(Object.class)) else if (clazz.equals(Object.class))
return clazz; return clazz;

View File

@ -26,8 +26,6 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import net.minecraft.server.Packet;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -501,7 +499,7 @@ public class PlayerInjectionHandler {
* @throws IllegalAccessException If the reflection machinery failed. * @throws IllegalAccessException If the reflection machinery failed.
* @throws InvocationTargetException If the underlying method caused an error. * @throws InvocationTargetException If the underlying method caused an error.
*/ */
public void processPacket(Player player, Packet mcPacket) throws IllegalAccessException, InvocationTargetException { public void processPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
PlayerInjector injector = getInjector(player); PlayerInjector injector = getInjector(player);

View File

@ -25,12 +25,8 @@ import java.lang.reflect.Method;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress; import java.net.SocketAddress;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.NetLoginHandler;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.Factory;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.comphenix.protocol.Packets; import com.comphenix.protocol.Packets;
@ -38,6 +34,7 @@ import com.comphenix.protocol.error.ErrorReporter;
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.events.PacketListener; import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.injector.GamePhase; import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker; import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks; import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
@ -45,6 +42,7 @@ import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.utility.MinecraftReflection;
abstract class PlayerInjector { abstract class PlayerInjector {
@ -122,9 +120,9 @@ abstract class PlayerInjector {
* @param player - the player to retrieve. * @param player - the player to retrieve.
* @return Notch player object. * @return Notch player object.
*/ */
protected EntityPlayer getEntityPlayer(Player player) { protected Object getEntityPlayer(Player player) {
CraftPlayer craft = (CraftPlayer) player; BukkitUnwrapper unwrapper = new BukkitUnwrapper();
return craft.getHandle(); return unwrapper.unwrapItem(player);
} }
/** /**
@ -138,7 +136,7 @@ abstract class PlayerInjector {
//Dispatch to the correct injection method //Dispatch to the correct injection method
if (injectionSource instanceof Player) if (injectionSource instanceof Player)
initializePlayer(injectionSource); initializePlayer(injectionSource);
else if (injectionSource instanceof NetLoginHandler) else if (MinecraftReflection.isLoginHandler(injectionSource))
initializeLogin(injectionSource); initializeLogin(injectionSource);
else else
throw new IllegalArgumentException("Cannot initialize a player hook using a " + injectionSource.getClass().getName()); throw new IllegalArgumentException("Cannot initialize a player hook using a " + injectionSource.getClass().getName());
@ -150,7 +148,7 @@ abstract class PlayerInjector {
*/ */
public void initializePlayer(Object player) { public void initializePlayer(Object player) {
EntityPlayer notchEntity = getEntityPlayer((Player) player); Object notchEntity = getEntityPlayer((Player) player);
if (!hasInitialized) { if (!hasInitialized) {
// Do this first, in case we encounter an exception // Do this first, in case we encounter an exception
@ -204,7 +202,7 @@ abstract class PlayerInjector {
// And the queue method // And the queue method
if (queueMethod == null) if (queueMethod == null)
queueMethod = FuzzyReflection.fromClass(reference.getType()). queueMethod = FuzzyReflection.fromClass(reference.getType()).
getMethodByParameters("queue", Packet.class ); getMethodByParameters("queue", MinecraftReflection.getPacketClass());
// And the data input stream that we'll use to identify a player // And the data input stream that we'll use to identify a player
if (inputField == null) if (inputField == null)
@ -323,7 +321,7 @@ abstract class PlayerInjector {
} }
} }
private Field getProxyField(EntityPlayer notchEntity, Field serverField) { private Field getProxyField(Object notchEntity, Field serverField) {
try { try {
Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true); Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true);
@ -379,7 +377,7 @@ abstract class PlayerInjector {
try { try {
// Well, that sucks. Try just Minecraft objects then. // Well, that sucks. Try just Minecraft objects then.
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true). netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
getFieldByType(FuzzyReflection.MINECRAFT_OBJECT); getFieldByType(MinecraftReflection.MINECRAFT_OBJECT);
} catch (RuntimeException e2) { } catch (RuntimeException e2) {
throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage()); throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage());
@ -398,10 +396,10 @@ abstract class PlayerInjector {
* @return The stored entity player. * @return The stored entity player.
* @throws IllegalAccessException If the reflection failed. * @throws IllegalAccessException If the reflection failed.
*/ */
private EntityPlayer getEntityPlayer(Object netHandler) throws IllegalAccessException { private Object getEntityPlayer(Object netHandler) throws IllegalAccessException {
if (entityPlayerField == null) if (entityPlayerField == null)
entityPlayerField = FuzzyReflection.fromObject(netHandler).getFieldByType(".*EntityPlayer"); entityPlayerField = FuzzyReflection.fromObject(netHandler).getFieldByType(".*EntityPlayer");
return (EntityPlayer) FieldUtils.readField(entityPlayerField, netHandler); return FieldUtils.readField(entityPlayerField, netHandler);
} }
/** /**
@ -410,15 +408,15 @@ abstract class PlayerInjector {
* @throws IllegalAccessException If the reflection machinery failed. * @throws IllegalAccessException If the reflection machinery failed.
* @throws InvocationTargetException If the underlying method caused an error. * @throws InvocationTargetException If the underlying method caused an error.
*/ */
public void processPacket(Packet packet) throws IllegalAccessException, InvocationTargetException { public void processPacket(Object packet) throws IllegalAccessException, InvocationTargetException {
Object netHandler = getNetHandler(); Object netHandler = getNetHandler();
// Get the process method // Get the process method
if (processMethod == null) { if (processMethod == null) {
try { try {
processMethod = FuzzyReflection.fromClass(Packet.class). processMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
getMethodByParameters("processPacket", netHandlerField.getType()); getMethodByParameters("processPacket", netHandlerField.getType());
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw new IllegalArgumentException("Cannot locate process packet method: " + e.getMessage()); throw new IllegalArgumentException("Cannot locate process packet method: " + e.getMessage());
} }
@ -440,7 +438,7 @@ abstract class PlayerInjector {
* @param filtered - whether or not the packet will be filtered by our listeners. * @param filtered - whether or not the packet will be filtered by our listeners.
* @param InvocationTargetException If an error occured when sending the packet. * @param InvocationTargetException If an error occured when sending the packet.
*/ */
public abstract void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException; public abstract void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException;
/** /**
* Inject a hook to catch packets sent to the current player. * Inject a hook to catch packets sent to the current player.
@ -501,7 +499,7 @@ abstract class PlayerInjector {
* @param packet - packet to sent. * @param packet - packet to sent.
* @return The given packet, or the packet replaced by the listeners. * @return The given packet, or the packet replaced by the listeners.
*/ */
public Packet handlePacketSending(Packet packet) { public Object handlePacketSending(Object packet) {
try { try {
// Get the packet ID too // Get the packet ID too
Integer id = invoker.getPacketID(packet); Integer id = invoker.getPacketID(packet);
@ -516,7 +514,7 @@ abstract class PlayerInjector {
if (updateOnLogin) { if (updateOnLogin) {
if (id == Packets.Server.LOGIN) { if (id == Packets.Server.LOGIN) {
try { try {
updatedPlayer = getEntityPlayer(getNetHandler()).getBukkitEntity(); updatedPlayer = (Player) MinecraftReflection.getBukkitEntity(getEntityPlayer(getNetHandler()));
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
reporter.reportDetailed(this, "Cannot update player in PlayerEvent.", e, packet); reporter.reportDetailed(this, "Cannot update player in PlayerEvent.", e, packet);
} }

View File

@ -234,7 +234,8 @@ class Metrics {
* *
* @return True if statistics measuring is running, otherwise false. * @return True if statistics measuring is running, otherwise false.
*/ */
public boolean start() { @SuppressWarnings("deprecation")
public boolean start() {
synchronized (optOutLock) { synchronized (optOutLock) {
// Did we opt out? // Did we opt out?
if (isOptOut()) { if (isOptOut()) {

View File

@ -32,11 +32,6 @@ import java.util.regex.Pattern;
* @author Kristian * @author Kristian
*/ */
public class FuzzyReflection { public class FuzzyReflection {
/**
* Matches a Minecraft object.
*/
public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+";
// The class we're actually representing // The class we're actually representing
private Class<?> source; private Class<?> source;

View File

@ -0,0 +1,46 @@
package com.comphenix.protocol.utility;
import java.util.Map;
import com.google.common.collect.Maps;
/**
* Represents a dynamic package and an arbitrary number of cached classes.
*
* @author Kristian
*/
class CachedPackage {
private Map<String, Class<?>> cache;
private String packageName;
public CachedPackage(String packageName) {
this.packageName = packageName;
this.cache = Maps.newConcurrentMap();
}
/**
* Retrieve the class object of a specific class in the current package.
* @param className - the specific class.
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
@SuppressWarnings("rawtypes")
public Class getPackageClass(String className) {
try {
Class result = cache.get(className);
// Concurrency is not a problem - we don't care if we look up a class twice
if (result == null) {
// Look up the class dynamically
result = CachedPackage.class.getClassLoader().
loadClass(packageName + "." + className);
cache.put(className, result);
}
return result;
} catch (ClassNotFoundException e) {
throw new RuntimeException("Cannot find class " + className, e);
}
}
}

View File

@ -0,0 +1,463 @@
package com.comphenix.protocol.utility;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import javax.annotation.Nonnull;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.injector.BukkitUnwrapper;
/**
* Methods and constants specifically used in conjuction with reflecting Minecraft object.
*
* @author Kristian
*/
public class MinecraftReflection {
/**
* Regular expression that matches a Minecraft object.
*/
public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+";
/**
* The package name of all the classes that belongs to the native code in Minecraft.
*/
private static String MINECRAFT_PREFIX_PACKAGE = "net.minecraft.server";
private static String MINECRAFT_FULL_PACKAGE = null;
private static String CRAFTBUKKIT_PACKAGE = null;
private static CachedPackage minecraftPackage;
private static CachedPackage craftbukkitPackage;
// org.bukkit.craftbukkit
private static Class<?> craftItemStackClass;
private static Constructor<?> craftNMSConstructor;
private static Constructor<?> craftBukkitConstructor;
// net.minecraft.server
private static Class<?> itemStackArrayClass;
/**
* Retrieve the name of the Minecraft server package.
* @return Full canonical name of the Minecraft server package.
*/
public static String getMinecraftPackage() {
// Speed things up
if (MINECRAFT_FULL_PACKAGE != null)
return MINECRAFT_FULL_PACKAGE;
Server craftServer = Bukkit.getServer();
// This server should have a "getHandle" method that we can use
if (craftServer != null) {
try {
Class<?> craftClass = craftServer.getClass();
Method getHandle = craftClass.getMethod("getHandle");
Class<?> returnType = getHandle.getReturnType();
String returnName = returnType.getCanonicalName();
// The return type will tell us the full package, regardless of formating
CRAFTBUKKIT_PACKAGE = getPackage(craftClass.getCanonicalName());
MINECRAFT_FULL_PACKAGE = getPackage(returnName);
return MINECRAFT_FULL_PACKAGE;
} catch (SecurityException e) {
throw new RuntimeException("Security violation. Cannot get handle method.", e);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Cannot find getHandle() method on server. Is this a modified CraftBukkit version?", e);
}
} else {
throw new IllegalStateException("Could not find Bukkit. Is it running?");
}
}
/**
* Used during debugging and testing.
* @param minecraftPackage - the current Minecraft package.
* @param craftBukkitPackage - the current CraftBukkit package.
*/
public static void setMinecraftPackage(String minecraftPackage, String craftBukkitPackage) {
MINECRAFT_FULL_PACKAGE = minecraftPackage;
CRAFTBUKKIT_PACKAGE = craftBukkitPackage;
}
/**
* Retrieve the name of the root CraftBukkit package.
* @return Full canonical name of the root CraftBukkit package.
*/
public static String getCraftBukkitPackage() {
// Ensure it has been initialized
getMinecraftPackage();
return CRAFTBUKKIT_PACKAGE;
}
/**
* Retrieve the package name from a given canonical Java class name.
* @param fullName - full Java class name.
* @return The package name.
*/
private static String getPackage(String fullName) {
return fullName.substring(0, fullName.lastIndexOf("."));
}
/**
* Determine if a given object can be found within the package net.minecraft.server.
* @param obj - the object to test.
* @return TRUE if it can, FALSE otherwise.
*/
public static boolean isMinecraftObject(@Nonnull Object obj) {
if (obj == null)
throw new IllegalArgumentException("Cannot determine the type of a null object.");
// Doesn't matter if we don't check for the version here
return obj.getClass().getName().startsWith(MINECRAFT_PREFIX_PACKAGE);
}
/**
* Determine if a given object is found in net.minecraft.server, and has the given name.
* @param obj - the object to test.
* @param className - the class name to test.
* @return TRUE if it can, FALSE otherwise.
*/
public static boolean isMinecraftObject(@Nonnull Object obj, String className) {
if (obj == null)
throw new IllegalArgumentException("Cannot determine the type of a null object.");
String javaName = obj.getClass().getName();
return javaName.startsWith(MINECRAFT_PREFIX_PACKAGE) && javaName.endsWith(className);
}
/**
* Dynamically retrieve the Bukkit entity from a given entity.
* @param nmsObject - the NMS entity.
* @return A bukkit entity.
* @throws RuntimeException If we were unable to retrieve the Bukkit entity.
*/
public static Object getBukkitEntity(Object nmsObject) {
if (nmsObject == null)
return null;
// We will have to do this dynamically, unfortunately
try {
return nmsObject.getClass().getMethod("getBukkitEntity").invoke(nmsObject);
} catch (Exception e) {
throw new RuntimeException("Cannot get Bukkit entity from " + nmsObject, e);
}
}
/**
* Determine if a given object is a ChunkPosition.
* @param obj - the object to test.
* @return TRUE if it can, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isChunkPosition(Object obj) {
return getChunkPositionClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if a given object is a ChunkCoordinate.
* @param obj - the object to test.
* @return TRUE if it can, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isChunkCoordinates(Object obj) {
return getChunkCoordinatesClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is actually a Minecraft packet.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isPacketClass(Object obj) {
return getPacketClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is a NetLoginHandler.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isLoginHandler(Object obj) {
return getNetLoginHandlerClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is actually a Minecraft packet.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isMinecraftEntity(Object obj) {
return getEntityClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is a NMS ItemStack.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isItemStack(Object value) {
return getItemStackClass().isAssignableFrom(value.getClass());
}
/**
* Determine if the given object is a Minecraft player entity.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isMinecraftPlayer(Object obj) {
return getEntityPlayerClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is a watchable object.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isWatchableObject(Object obj) {
return getWatchableObjectClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is a data watcher object.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isDataWatcher(Object obj) {
return getDataWatcherClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is a CraftItemStack instancey.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isCraftItemStack(Object obj) {
return getCraftItemStackClass().isAssignableFrom(obj.getClass());
}
/**
* Retrieve the EntityPlayer (NMS) class.
* @return The entity class.
*/
@SuppressWarnings("rawtypes")
public static Class getEntityPlayerClass() {
return getMinecraftClass("EntityPlayer");
}
/**
* Retrieve the entity (NMS) class.
* @return The entity class.
*/
@SuppressWarnings("rawtypes")
public static Class getEntityClass() {
return getMinecraftClass("Entity");
}
/**
* Retrieve the packet class.
* @return The packet class.
*/
@SuppressWarnings("rawtypes")
public static Class getPacketClass() {
return getMinecraftClass("Packet");
}
/**
* Retrieve the NetLoginHandler class.
* @return The NetLoginHandler class.
*/
@SuppressWarnings("rawtypes")
public static Class getNetLoginHandlerClass() {
return getMinecraftClass("NetLoginHandler");
}
/**
* Retrieve the NetLoginHandler class.
* @return The NetLoginHandler class.
*/
@SuppressWarnings("rawtypes")
public static Class getItemStackClass() {
return getMinecraftClass("ItemStack");
}
/**
* Retrieve the WorldType class.
* @return The WorldType class.
*/
@SuppressWarnings("rawtypes")
public static Class getWorldTypeClass() {
return getMinecraftClass("WorldType");
}
/**
* Retrieve the DataWatcher class.
* @return The DataWatcher class.
*/
@SuppressWarnings("rawtypes")
public static Class getDataWatcherClass() {
return getMinecraftClass("DataWatcher");
}
/**
* Retrieve the ChunkPosition class.
* @return The ChunkPosition class.
*/
@SuppressWarnings("rawtypes")
public static Class getChunkPositionClass() {
return getMinecraftClass("ChunkPosition");
}
/**
* Retrieve the ChunkPosition class.
* @return The ChunkPosition class.
*/
@SuppressWarnings("rawtypes")
public static Class getChunkCoordinatesClass() {
return getMinecraftClass("ChunkCoordinates");
}
/**
* Retrieve the WatchableObject class.
* @return The WatchableObject class.
*/
@SuppressWarnings("rawtypes")
public static Class getWatchableObjectClass() {
return getMinecraftClass("WatchableObject");
}
/**
* Retrieve the ItemStack[] class.
* @return The ItemStack[] class.
*/
@SuppressWarnings("rawtypes")
public static Class getItemStackArrayClass() {
if (itemStackArrayClass == null)
itemStackArrayClass = getArrayClass(getItemStackClass());
return itemStackArrayClass;
}
/**
* Retrieve the array class of a given component type.
* @param componentType - type of each element in the array.
* @return The class of the array.
*/
@SuppressWarnings("rawtypes")
public static Class getArrayClass(Class componentType) {
// Bit of a hack, but it works
return Array.newInstance(componentType, 0).getClass();
}
/**
* Retrieve the CraftItemStack class.
* @return The CraftItemStack class.
*/
@SuppressWarnings("rawtypes")
public static Class getCraftItemStackClass() {
if (craftItemStackClass == null)
craftItemStackClass = getCraftBukkitClass("inventory.CraftItemStack");
return craftItemStackClass;
}
/**
* Retrieve a CraftItemStack from a given ItemStack.
* @param bukkitItemStack - the Bukkit ItemStack to convert.
* @return A CraftItemStack as an ItemStack.
*/
@SuppressWarnings("unchecked")
public static ItemStack getBukkitItemStack(ItemStack bukkitItemStack) {
if (craftBukkitConstructor == null) {
try {
craftBukkitConstructor = getCraftItemStackClass().getConstructor(ItemStack.class);
} catch (Exception e) {
throw new RuntimeException("Cannot find CraftItemStack(org.bukkit.inventory.ItemStack).", e);
}
}
// Try to create the CraftItemStack
try {
return (ItemStack) craftBukkitConstructor.newInstance(bukkitItemStack);
} catch (Exception e) {
throw new RuntimeException("Cannot construct CraftItemStack.", e);
}
}
/**
* Retrieve the Bukkit ItemStack from a given net.minecraft.server ItemStack.
* @param minecraftItemStack - the NMS ItemStack to wrap.
* @return The wrapped ItemStack.
*/
@SuppressWarnings("unchecked")
public static ItemStack getBukkitItemStack(Object minecraftItemStack) {
if (craftNMSConstructor == null) {
try {
craftNMSConstructor = getCraftItemStackClass().getConstructor(minecraftItemStack.getClass());
} catch (Exception e) {
throw new RuntimeException("Cannot find CraftItemStack(net.mineraft.server.ItemStack).", e);
}
}
// Try to create the CraftItemStack
try {
return (ItemStack) craftNMSConstructor.newInstance(minecraftItemStack);
} catch (Exception e) {
throw new RuntimeException("Cannot construct CraftItemStack.", e);
}
}
/**
* Retrieve the net.minecraft.server ItemStack from a Bukkit ItemStack.
* @param stack - the Bukkit ItemStack to convert.
* @return The NMS ItemStack.
*/
public static Object getMinecraftItemStack(ItemStack stack) {
// Make sure this is a CraftItemStack
if (!isCraftItemStack(stack))
stack = getBukkitItemStack(stack);
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
return unwrapper.unwrapItem(stack);
}
/**
* Retrieve the class object of a specific CraftBukkit class.
* @param className - the specific CraftBukkit class.
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
@SuppressWarnings("rawtypes")
public static Class getCraftBukkitClass(String className) {
if (craftbukkitPackage == null)
craftbukkitPackage = new CachedPackage(getCraftBukkitPackage());
return craftbukkitPackage.getPackageClass(className);
}
/**
* Retrieve the class object of a specific Minecraft class.
* @param className - the specific Minecraft class.
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
@SuppressWarnings("rawtypes")
public static Class getMinecraftClass(String className) {
if (minecraftPackage == null)
minecraftPackage = new CachedPackage(getMinecraftPackage());
return minecraftPackage.getPackageClass(className);
}
}

View File

@ -1,16 +1,13 @@
package com.comphenix.protocol.wrappers; package com.comphenix.protocol.wrappers;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import net.minecraft.server.DataWatcher;
import net.minecraft.server.WatchableObject;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.WorldType; import org.bukkit.WorldType;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -19,6 +16,7 @@ import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
/** /**
* Contains several useful equivalent converters for normal Bukkit types. * Contains several useful equivalent converters for normal Bukkit types.
@ -29,9 +27,13 @@ public class BukkitConverters {
// Check whether or not certain classes exists // Check whether or not certain classes exists
private static boolean hasWorldType = false; private static boolean hasWorldType = false;
// Used to access the world type
private static Method worldTypeName;
private static Method worldTypeGetType;
static { static {
try { try {
Class.forName("net.minecraft.server.WorldType"); Class.forName(MinecraftReflection.getMinecraftPackage() + ".WorldType");
hasWorldType = true; hasWorldType = true;
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
} }
@ -100,8 +102,8 @@ public class BukkitConverters {
} }
public WrappedWatchableObject getSpecific(Object generic) { public WrappedWatchableObject getSpecific(Object generic) {
if (generic instanceof WatchableObject) if (MinecraftReflection.isWatchableObject(generic))
return new WrappedWatchableObject((WatchableObject) generic); return new WrappedWatchableObject(generic);
else if (generic instanceof WrappedWatchableObject) else if (generic instanceof WrappedWatchableObject)
return (WrappedWatchableObject) generic; return (WrappedWatchableObject) generic;
else else
@ -128,8 +130,8 @@ public class BukkitConverters {
@Override @Override
public WrappedDataWatcher getSpecific(Object generic) { public WrappedDataWatcher getSpecific(Object generic) {
if (generic instanceof DataWatcher) if (MinecraftReflection.isDataWatcher(generic))
return new WrappedDataWatcher((DataWatcher) generic); return new WrappedDataWatcher(generic);
else if (generic instanceof WrappedDataWatcher) else if (generic instanceof WrappedDataWatcher)
return (WrappedDataWatcher) generic; return (WrappedDataWatcher) generic;
else else
@ -153,15 +155,35 @@ public class BukkitConverters {
return null; return null;
return getIgnoreNull(new EquivalentConverter<WorldType>() { return getIgnoreNull(new EquivalentConverter<WorldType>() {
@SuppressWarnings("unchecked")
@Override @Override
public Object getGeneric(Class<?> genericType, WorldType specific) { public Object getGeneric(Class<?> genericType, WorldType specific) {
return net.minecraft.server.WorldType.getType(specific.getName()); try {
if (worldTypeGetType == null)
worldTypeGetType = MinecraftReflection.getWorldTypeClass().getMethod("getType", String.class);
// Convert to the Bukkit world type
return worldTypeGetType.invoke(this, specific.getName());
} catch (Exception e) {
throw new FieldAccessException("Cannot find the WorldType.getType() method.", e);
}
} }
@SuppressWarnings("unchecked")
@Override @Override
public WorldType getSpecific(Object generic) { public WorldType getSpecific(Object generic) {
net.minecraft.server.WorldType type = (net.minecraft.server.WorldType) generic; try {
return WorldType.getByName(type.name()); if (worldTypeName == null)
worldTypeName = MinecraftReflection.getWorldTypeClass().getMethod("name");
// Dynamically call the namne method
String name = (String) worldTypeName.invoke(generic);
return WorldType.getByName(name);
} catch (Exception e) {
throw new FieldAccessException("Cannot call the name method in WorldType.", e);
}
} }
@Override @Override
@ -221,12 +243,12 @@ public class BukkitConverters {
public static EquivalentConverter<ItemStack> getItemStackConverter() { public static EquivalentConverter<ItemStack> getItemStackConverter() {
return getIgnoreNull(new EquivalentConverter<ItemStack>() { return getIgnoreNull(new EquivalentConverter<ItemStack>() {
public Object getGeneric(Class<?> genericType, ItemStack specific) { public Object getGeneric(Class<?> genericType, ItemStack specific) {
return toStackNMS(specific); return MinecraftReflection.getMinecraftItemStack(specific);
} }
@Override @Override
public ItemStack getSpecific(Object generic) { public ItemStack getSpecific(Object generic) {
return new CraftItemStack((net.minecraft.server.ItemStack) generic); return MinecraftReflection.getBukkitItemStack(generic);
} }
@Override @Override
@ -236,20 +258,6 @@ public class BukkitConverters {
}); });
} }
/**
* Convert an item stack to the NMS equivalent.
* @param stack - Bukkit stack to convert.
* @return A bukkit stack.
*/
private static net.minecraft.server.ItemStack toStackNMS(ItemStack stack) {
// We must be prepared for an object that simply implements ItemStcak
if (stack instanceof CraftItemStack) {
return ((CraftItemStack) stack).getHandle();
} else {
return (new CraftItemStack(stack)).getHandle();
}
}
/** /**
* Wraps a given equivalent converter in NULL checks, ensuring that such values are ignored. * Wraps a given equivalent converter in NULL checks, ensuring that such values are ignored.
* @param delegate - the underlying equivalent converter. * @param delegate - the underlying equivalent converter.

View File

@ -1,10 +1,13 @@
package com.comphenix.protocol.wrappers; package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects; import com.google.common.base.Objects;
/** /**
@ -19,6 +22,8 @@ public class ChunkPosition {
*/ */
public static ChunkPosition ORIGIN = new ChunkPosition(0, 0, 0); public static ChunkPosition ORIGIN = new ChunkPosition(0, 0, 0);
private static Constructor<?> chunkPositionConstructor;
// Use protected members, like Bukkit // Use protected members, like Bukkit
protected final int x; protected final int x;
protected final int y; protected final int y;
@ -128,27 +133,35 @@ public class ChunkPosition {
*/ */
public static EquivalentConverter<ChunkPosition> getConverter() { public static EquivalentConverter<ChunkPosition> getConverter() {
return new EquivalentConverter<ChunkPosition>() { return new EquivalentConverter<ChunkPosition>() {
@SuppressWarnings("unchecked")
@Override @Override
public Object getGeneric(Class<?> genericType, ChunkPosition specific) { public Object getGeneric(Class<?> genericType, ChunkPosition specific) {
return new net.minecraft.server.ChunkPosition(specific.x, specific.y, specific.z); if (chunkPositionConstructor == null) {
try {
chunkPositionConstructor = MinecraftReflection.getChunkPositionClass().
getConstructor(int.class, int.class, int.class);
} catch (Exception e) {
throw new RuntimeException("Cannot find chunk position constructor.", e);
}
}
// Construct the underlying ChunkPosition
try {
return chunkPositionConstructor.newInstance(specific.x, specific.y, specific.z);
} catch (Exception e) {
throw new RuntimeException("Cannot construct ChunkPosition.", e);
}
} }
@Override @Override
public ChunkPosition getSpecific(Object generic) { public ChunkPosition getSpecific(Object generic) {
if (generic instanceof net.minecraft.server.ChunkPosition) { if (MinecraftReflection.isChunkPosition(generic)) {
net.minecraft.server.ChunkPosition other = (net.minecraft.server.ChunkPosition) generic; // Use a structure modifier
intModifier = new StructureModifier<Object>(generic.getClass(), null, false).withType(int.class);
try { // Damn it all
if (intModifier == null) if (intModifier.size() < 3) {
return new ChunkPosition(other.x, other.y, other.z); throw new IllegalStateException("Cannot read class " + generic.getClass() + " for its integer fields.");
} catch (LinkageError e) {
// It could happen. If it does, use a structure modifier instead
intModifier = new StructureModifier<Object>(other.getClass(), null, false).withType(int.class);
// Damn it all
if (intModifier.size() < 3) {
throw new IllegalStateException("Cannot read class " + other.getClass() + " for its integer fields.");
}
} }
if (intModifier.size() >= 3) { if (intModifier.size() >= 3) {

View File

@ -1,9 +1,9 @@
package com.comphenix.protocol.wrappers; package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import net.minecraft.server.ChunkCoordinates;
/** /**
* Allows access to a chunk coordinate. * Allows access to a chunk coordinate.
* *
@ -16,23 +16,42 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
*/ */
private static final boolean LARGER_THAN_NULL = true; private static final boolean LARGER_THAN_NULL = true;
protected ChunkCoordinates handle; @SuppressWarnings("rawtypes")
protected Comparable handle;
// Used to access a ChunkCoordinate
private static StructureModifier<Integer> intModifier;
/** /**
* Create a new empty wrapper. * Create a new empty wrapper.
*/ */
@SuppressWarnings("rawtypes")
public WrappedChunkCoordinate() { public WrappedChunkCoordinate() {
this(new ChunkCoordinates()); try {
this.handle = (Comparable) MinecraftReflection.getChunkCoordinatesClass().newInstance();
initializeModifier();
} catch (Exception e) {
throw new RuntimeException("Cannot construct chunk coordinate.");
}
} }
/** /**
* Create a wrapper for a specific chunk coordinates. * Create a wrapper for a specific chunk coordinates.
* @param handle - the NMS chunk coordinates. * @param handle - the NMS chunk coordinates.
*/ */
public WrappedChunkCoordinate(ChunkCoordinates handle) { @SuppressWarnings("rawtypes")
public WrappedChunkCoordinate(Comparable handle) {
if (handle == null) if (handle == null)
throw new IllegalArgumentException("handle cannot be NULL"); throw new IllegalArgumentException("handle cannot be NULL");
this.handle = handle; this.handle = handle;
initializeModifier();
}
// Ensure that the structure modifier is initialized
private void initializeModifier() {
if (intModifier == null) {
intModifier = new StructureModifier<Object>(handle.getClass(), null, false).withType(int.class);
}
} }
/** /**
@ -56,7 +75,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
this(position.getX(), position.getY(), position.getZ()); this(position.getX(), position.getY(), position.getZ());
} }
public ChunkCoordinates getHandle() { public Object getHandle() {
return handle; return handle;
} }
@ -65,7 +84,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
* @return The x coordinate. * @return The x coordinate.
*/ */
public int getX() { public int getX() {
return handle.x; return intModifier.read(0);
} }
/** /**
@ -73,7 +92,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
* @param newX - the new x coordinate. * @param newX - the new x coordinate.
*/ */
public void setX(int newX) { public void setX(int newX) {
handle.x = newX; intModifier.write(0, newX);
} }
/** /**
@ -81,7 +100,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
* @return The y coordinate. * @return The y coordinate.
*/ */
public int getY() { public int getY() {
return handle.y; return intModifier.read(1);
} }
/** /**
@ -89,7 +108,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
* @param newY - the new y coordinate. * @param newY - the new y coordinate.
*/ */
public void setY(int newY) { public void setY(int newY) {
handle.y = newY; intModifier.write(1, newY);
} }
/** /**
@ -97,7 +116,15 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
* @return The z coordinate. * @return The z coordinate.
*/ */
public int getZ() { public int getZ() {
return handle.z; return intModifier.read(2);
}
/**
* Set the z coordinate of the underlying coordiate.
* @param newZ - the new z coordinate.
*/
public void setZ(int newZ) {
intModifier.write(2, newZ);
} }
/** /**
@ -108,14 +135,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
return new ChunkPosition(getX(), getY(), getZ()); return new ChunkPosition(getX(), getY(), getZ());
} }
/** @SuppressWarnings("unchecked")
* Set the z coordinate of the underlying coordiate.
* @param newZ - the new z coordinate.
*/
public void setZ(int newZ) {
handle.z = newZ;
}
@Override @Override
public int compareTo(WrappedChunkCoordinate other) { public int compareTo(WrappedChunkCoordinate other) {
// We'll handle NULL objects too, unlike ChunkCoordinates // We'll handle NULL objects too, unlike ChunkCoordinates

View File

@ -6,6 +6,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -13,6 +14,8 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.annotation.Nullable;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -20,17 +23,17 @@ import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.FieldAccessException; 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.utility.MinecraftReflection;
import com.google.common.base.Function;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.collect.Iterators;
import net.minecraft.server.DataWatcher;
import net.minecraft.server.WatchableObject;
/** /**
* Wraps a DataWatcher that is used to transmit arbitrary key-value pairs with a given entity. * Wraps a DataWatcher that is used to transmit arbitrary key-value pairs with a given entity.
* *
* @author Kristian * @author Kristian
*/ */
public class WrappedDataWatcher { public class WrappedDataWatcher implements Iterable<WrappedWatchableObject> {
/** /**
* Used to assign integer IDs to given types. * Used to assign integer IDs to given types.
@ -55,7 +58,7 @@ public class WrappedDataWatcher {
private static boolean hasInitialized; private static boolean hasInitialized;
// The underlying DataWatcher we're modifying // The underlying DataWatcher we're modifying
protected DataWatcher handle; protected Object handle;
// Lock // Lock
private ReadWriteLock readWriteLock; private ReadWriteLock readWriteLock;
@ -69,7 +72,13 @@ public class WrappedDataWatcher {
*/ */
public WrappedDataWatcher() { public WrappedDataWatcher() {
// Just create a new watcher // Just create a new watcher
this(new DataWatcher()); try {
this.handle = MinecraftReflection.getDataWatcherClass().newInstance();
initialize();
} catch (Exception e) {
throw new RuntimeException("Unable to construct DataWatcher.", e);
}
} }
/** /**
@ -77,27 +86,42 @@ public class WrappedDataWatcher {
* @param handle - the data watcher to wrap. * @param handle - the data watcher to wrap.
* @throws FieldAccessException If we're unable to wrap a DataWatcher. * @throws FieldAccessException If we're unable to wrap a DataWatcher.
*/ */
public WrappedDataWatcher(DataWatcher handle) { public WrappedDataWatcher(Object handle) {
this.handle = handle; if (handle == null)
throw new IllegalArgumentException("Handle cannot be NULL.");
if (!MinecraftReflection.isDataWatcher(handle))
throw new IllegalArgumentException("The value " + handle + " is not a DataWatcher.");
try { this.handle = handle;
initialize(); initialize();
} catch (FieldAccessException e) {
throw new RuntimeException("Cannot initialize wrapper.", e);
}
} }
/** /**
* Create a new data watcher from a list of watchable objects. * Create a new data watcher for a list of watchable objects.
* <p>
* Note that the watchable objects are not cloned, and will be modified in place. Use "deepClone" if
* that is not desirable.
* <p>
* The {@link #removeObject(int)} method will not modify the given list, however.
*
* @param watchableObjects - list of watchable objects that will be copied. * @param watchableObjects - list of watchable objects that will be copied.
* @throws FieldAccessException Unable to read watchable objects. * @throws FieldAccessException Unable to read watchable objects.
*/ */
public WrappedDataWatcher(List<WrappedWatchableObject> watchableObjects) throws FieldAccessException { public WrappedDataWatcher(List<WrappedWatchableObject> watchableObjects) throws FieldAccessException {
this(); this();
Lock writeLock = getReadWriteLock().writeLock();
Map<Integer, Object> map = getWatchableObjectMap();
// Fill the underlying map writeLock.lock();
for (WrappedWatchableObject watched : watchableObjects) {
setObject(watched.getIndex(), watched.getValue()); try {
// Add the watchable objects by reference
for (WrappedWatchableObject watched : watchableObjects) {
map.put(watched.getIndex(), watched.handle);
}
} finally {
writeLock.unlock();
} }
} }
@ -105,7 +129,7 @@ public class WrappedDataWatcher {
* Retrieves the underlying data watcher. * Retrieves the underlying data watcher.
* @return The underlying data watcher. * @return The underlying data watcher.
*/ */
public DataWatcher getHandle() { public Object getHandle() {
return handle; return handle;
} }
@ -215,7 +239,7 @@ public class WrappedDataWatcher {
*/ */
public Object getObject(int index) throws FieldAccessException { public Object getObject(int index) throws FieldAccessException {
// The get method will take care of concurrency // The get method will take care of concurrency
WatchableObject watchable = getWatchedObject(index); Object watchable = getWatchedObject(index);
if (watchable != null) { if (watchable != null) {
return new WrappedWatchableObject(watchable).getValue(); return new WrappedWatchableObject(watchable).getValue();
@ -230,15 +254,16 @@ public class WrappedDataWatcher {
* @throws FieldAccessException If reflection failed. * @throws FieldAccessException If reflection failed.
*/ */
public List<WrappedWatchableObject> getWatchableObjects() throws FieldAccessException { public List<WrappedWatchableObject> getWatchableObjects() throws FieldAccessException {
Lock readLock = getReadWriteLock().readLock();
readLock.lock();
try { try {
getReadWriteLock().readLock().lock();
List<WrappedWatchableObject> result = new ArrayList<WrappedWatchableObject>(); List<WrappedWatchableObject> result = new ArrayList<WrappedWatchableObject>();
// Add each watchable object to the list // Add each watchable object to the list
for (Object watchable : getWatchableObjectMap().values()) { for (Object watchable : getWatchableObjectMap().values()) {
if (watchable != null) { if (watchable != null) {
result.add(new WrappedWatchableObject((WatchableObject) watchable)); result.add(new WrappedWatchableObject(watchable));
} else { } else {
result.add(null); result.add(null);
} }
@ -246,7 +271,7 @@ public class WrappedDataWatcher {
return result; return result;
} finally { } finally {
getReadWriteLock().readLock().unlock(); readLock.unlock();
} }
} }
@ -266,6 +291,20 @@ public class WrappedDataWatcher {
} }
} }
/**
* Clone the content of the current DataWatcher.
* @return A cloned data watcher.
*/
public WrappedDataWatcher deepClone() {
WrappedDataWatcher clone = new WrappedDataWatcher();
// Make a new copy instead
for (WrappedWatchableObject watchable : this) {
clone.setObject(watchable.getIndex(), watchable.getClonedValue());
}
return clone;
}
/** /**
* Retrieve the number of watched objects. * Retrieve the number of watched objects.
* @return Watched object count. * @return Watched object count.
@ -282,6 +321,23 @@ public class WrappedDataWatcher {
} }
} }
/**
* Remove a given object from the underlying DataWatcher.
* @param index - index of the object to remove.
* @return The watchable object that was removed, or NULL If none could be found.
*/
public WrappedWatchableObject removeObject(int index) {
Lock writeLock = getReadWriteLock().writeLock();
writeLock.lock();
try {
Object removed = getWatchableObjectMap().remove(index);
return removed != null ? new WrappedWatchableObject(removed) : null;
} finally {
writeLock.unlock();
}
}
/** /**
* Set a watched byte. * Set a watched byte.
* @param index - index of the watched byte. * @param index - index of the watched byte.
@ -305,7 +361,7 @@ public class WrappedDataWatcher {
writeLock.lock(); writeLock.lock();
try { try {
WatchableObject watchable = getWatchedObject(index); Object watchable = getWatchedObject(index);
if (watchable != null) { if (watchable != null) {
new WrappedWatchableObject(watchable).setValue(newValue, update); new WrappedWatchableObject(watchable).setValue(newValue, update);
@ -325,18 +381,18 @@ public class WrappedDataWatcher {
} }
} }
private WatchableObject getWatchedObject(int index) throws FieldAccessException { private Object getWatchedObject(int index) throws FieldAccessException {
// We use the get-method first and foremost // We use the get-method first and foremost
if (getKeyValueMethod != null) { if (getKeyValueMethod != null) {
try { try {
return (WatchableObject) getKeyValueMethod.invoke(handle, index); return getKeyValueMethod.invoke(handle, index);
} catch (Exception e) { } catch (Exception e) {
throw new FieldAccessException("Cannot invoke get key method for index " + index, e); throw new FieldAccessException("Cannot invoke get key method for index " + index, e);
} }
} else { } else {
try { try {
getReadWriteLock().readLock().lock(); getReadWriteLock().readLock().lock();
return (WatchableObject) getWatchableObjectMap().get(index); return getWatchableObjectMap().get(index);
} finally { } finally {
getReadWriteLock().readLock().unlock(); getReadWriteLock().readLock().unlock();
@ -388,8 +444,8 @@ public class WrappedDataWatcher {
*/ */
public static WrappedDataWatcher getEntityWatcher(Entity entity) throws FieldAccessException { public static WrappedDataWatcher getEntityWatcher(Entity entity) throws FieldAccessException {
if (entityDataField == null) if (entityDataField == null)
entityDataField = FuzzyReflection.fromClass(net.minecraft.server.Entity.class, true). entityDataField = FuzzyReflection.fromClass(MinecraftReflection.getEntityClass(), true).
getFieldByType("datawatcher", DataWatcher.class); getFieldByType("datawatcher", MinecraftReflection.getDataWatcherClass());
BukkitUnwrapper unwrapper = new BukkitUnwrapper(); BukkitUnwrapper unwrapper = new BukkitUnwrapper();
@ -397,7 +453,7 @@ public class WrappedDataWatcher {
Object nsmWatcher = FieldUtils.readField(entityDataField, unwrapper.unwrapItem(entity), true); Object nsmWatcher = FieldUtils.readField(entityDataField, unwrapper.unwrapItem(entity), true);
if (nsmWatcher != null) if (nsmWatcher != null)
return new WrappedDataWatcher((DataWatcher) nsmWatcher); return new WrappedDataWatcher(nsmWatcher);
else else
return null; return null;
@ -417,7 +473,7 @@ public class WrappedDataWatcher {
else else
return; return;
FuzzyReflection fuzzy = FuzzyReflection.fromClass(DataWatcher.class, true); FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getDataWatcherClass(), true);
for (Field lookup : fuzzy.getFieldListByType(Map.class)) { for (Field lookup : fuzzy.getFieldListByType(Map.class)) {
if (Modifier.isStatic(lookup.getModifiers())) { if (Modifier.isStatic(lookup.getModifiers())) {
@ -476,4 +532,20 @@ public class WrappedDataWatcher {
// Use fallback method // Use fallback method
} }
} }
@Override
public Iterator<WrappedWatchableObject> iterator() {
// We'll wrap the iterator instead of creating a new list every time
return Iterators.transform(getWatchableObjectMap().values().iterator(),
new Function<Object, WrappedWatchableObject>() {
@Override
public WrappedWatchableObject apply(@Nullable Object item) {
if (item != null)
return new WrappedWatchableObject(item);
else
return null;
}
});
}
} }

View File

@ -1,13 +1,15 @@
package com.comphenix.protocol.wrappers; package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
import net.minecraft.server.ChunkCoordinates; import com.google.common.base.Objects;
import net.minecraft.server.ItemStack;
import net.minecraft.server.WatchableObject;
/** /**
* Represents a watchable object. * Represents a watchable object.
@ -22,7 +24,13 @@ public class WrappedWatchableObject {
// The field containing the value itself // The field containing the value itself
private static StructureModifier<Object> baseModifier; private static StructureModifier<Object> baseModifier;
protected WatchableObject handle; // Used to create new watchable objects
private static Constructor<?> watchableConstructor;
// The watchable object class type
private static Class<?> watchableObjectClass;
protected Object handle;
protected StructureModifier<Object> modifier; protected StructureModifier<Object> modifier;
// Type of the stored value // Type of the stored value
@ -32,7 +40,7 @@ public class WrappedWatchableObject {
* Wrap a given raw Minecraft watchable object. * Wrap a given raw Minecraft watchable object.
* @param handle - the raw watchable object to wrap. * @param handle - the raw watchable object to wrap.
*/ */
public WrappedWatchableObject(WatchableObject handle) { public WrappedWatchableObject(Object handle) {
load(handle); load(handle);
} }
@ -41,6 +49,7 @@ public class WrappedWatchableObject {
* @param index - the index. * @param index - the index.
* @param value - non-null value of specific types. * @param value - non-null value of specific types.
*/ */
@SuppressWarnings("unchecked")
public WrappedWatchableObject(int index, Object value) { public WrappedWatchableObject(int index, Object value) {
if (value == null) if (value == null)
throw new IllegalArgumentException("Value cannot be NULL."); throw new IllegalArgumentException("Value cannot be NULL.");
@ -49,24 +58,44 @@ public class WrappedWatchableObject {
Integer typeID = WrappedDataWatcher.getTypeID(value.getClass()); Integer typeID = WrappedDataWatcher.getTypeID(value.getClass());
if (typeID != null) { if (typeID != null) {
load(new WatchableObject(typeID, index, getUnwrapped(value))); if (watchableConstructor == null) {
try {
watchableConstructor = MinecraftReflection.getWatchableObjectClass().
getConstructor(int.class, int.class, Object.class);
} catch (Exception e) {
throw new RuntimeException("Cannot get the WatchableObject(int, int, Object) constructor.", e);
}
}
// Create the object
try {
load(watchableConstructor.newInstance(typeID, index, getUnwrapped(value)));
} catch (Exception e) {
throw new RuntimeException("Cannot construct underlying WatchableObject.", e);
}
} else { } else {
throw new IllegalArgumentException("Cannot watch the type " + value.getClass()); throw new IllegalArgumentException("Cannot watch the type " + value.getClass());
} }
} }
// Wrap a NMS object // Wrap a NMS object
private void load(WatchableObject handle) { private void load(Object handle) {
initialize(); initialize();
this.handle = handle; this.handle = handle;
this.modifier = baseModifier.withTarget(handle); this.modifier = baseModifier.withTarget(handle);
// Make sure the type is correct
if (!watchableObjectClass.isAssignableFrom(handle.getClass())) {
throw new ClassCastException("Cannot cast the class " + handle.getClass().getName() +
" to " + watchableObjectClass.getName());
}
} }
/** /**
* Retrieves the underlying watchable object. * Retrieves the underlying watchable object.
* @return The underlying watchable object. * @return The underlying watchable object.
*/ */
public WatchableObject getHandle() { public Object getHandle() {
return handle; return handle;
} }
@ -76,7 +105,8 @@ public class WrappedWatchableObject {
private static void initialize() { private static void initialize() {
if (!hasInitialized) { if (!hasInitialized) {
hasInitialized = true; hasInitialized = true;
baseModifier = new StructureModifier<Object>(WatchableObject.class, null, false); watchableObjectClass = MinecraftReflection.getWatchableObjectClass();
baseModifier = new StructureModifier<Object>(watchableObjectClass, null, false);
} }
} }
@ -204,12 +234,13 @@ public class WrappedWatchableObject {
* @param value - the raw NMS object to wrap. * @param value - the raw NMS object to wrap.
* @return The wrapped object. * @return The wrapped object.
*/ */
@SuppressWarnings("rawtypes")
static Object getWrapped(Object value) { static Object getWrapped(Object value) {
// Handle the special cases // Handle the special cases
if (value instanceof net.minecraft.server.ItemStack) { if (MinecraftReflection.isItemStack(value)) {
return BukkitConverters.getItemStackConverter().getSpecific(value); return BukkitConverters.getItemStackConverter().getSpecific(value);
} else if (value instanceof ChunkCoordinates) { } else if (MinecraftReflection.isChunkCoordinates(value)) {
return new WrappedChunkCoordinate((ChunkCoordinates) value); return new WrappedChunkCoordinate((Comparable) value);
} else { } else {
return value; return value;
} }
@ -221,9 +252,9 @@ public class WrappedWatchableObject {
* @return The wrapped class type. * @return The wrapped class type.
*/ */
static Class<?> getWrappedType(Class<?> unwrapped) { static Class<?> getWrappedType(Class<?> unwrapped) {
if (unwrapped.equals(net.minecraft.server.ChunkPosition.class)) if (unwrapped.equals(MinecraftReflection.getChunkPositionClass()))
return ChunkPosition.class; return ChunkPosition.class;
else if (unwrapped.equals(ChunkCoordinates.class)) else if (unwrapped.equals(MinecraftReflection.getChunkCoordinatesClass()))
return WrappedChunkCoordinate.class; return WrappedChunkCoordinate.class;
else else
return unwrapped; return unwrapped;
@ -240,7 +271,7 @@ public class WrappedWatchableObject {
return ((WrappedChunkCoordinate) wrapped).getHandle(); return ((WrappedChunkCoordinate) wrapped).getHandle();
else if (wrapped instanceof ItemStack) else if (wrapped instanceof ItemStack)
return BukkitConverters.getItemStackConverter().getGeneric( return BukkitConverters.getItemStackConverter().getGeneric(
net.minecraft.server.ItemStack.class, (org.bukkit.inventory.ItemStack) wrapped); MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
else else
return wrapped; return wrapped;
} }
@ -252,9 +283,9 @@ public class WrappedWatchableObject {
*/ */
static Class<?> getUnwrappedType(Class<?> wrapped) { static Class<?> getUnwrappedType(Class<?> wrapped) {
if (wrapped.equals(ChunkPosition.class)) if (wrapped.equals(ChunkPosition.class))
return net.minecraft.server.ChunkPosition.class; return MinecraftReflection.getChunkPositionClass();
else if (wrapped.equals(WrappedChunkCoordinate.class)) else if (wrapped.equals(WrappedChunkCoordinate.class))
return ChunkCoordinates.class; return MinecraftReflection.getChunkCoordinatesClass();
else else
return wrapped; return wrapped;
} }
@ -265,7 +296,8 @@ public class WrappedWatchableObject {
* @throws FieldAccessException If we're unable to use reflection. * @throws FieldAccessException If we're unable to use reflection.
*/ */
public WrappedWatchableObject deepClone() throws FieldAccessException { public WrappedWatchableObject deepClone() throws FieldAccessException {
WrappedWatchableObject clone = new WrappedWatchableObject(DefaultInstances.DEFAULT.getDefault(WatchableObject.class)); @SuppressWarnings("unchecked")
WrappedWatchableObject clone = new WrappedWatchableObject(DefaultInstances.DEFAULT.getDefault(MinecraftReflection.getWatchableObjectClass()));
clone.setDirtyState(getDirtyState()); clone.setDirtyState(getDirtyState());
clone.setIndex(getIndex()); clone.setIndex(getIndex());
@ -275,18 +307,48 @@ public class WrappedWatchableObject {
} }
// Helper // Helper
private Object getClonedValue() throws FieldAccessException { Object getClonedValue() throws FieldAccessException {
Object value = getValue(); Object value = getValue();
// Only a limited set of references types are supported // Only a limited set of references types are supported
if (value instanceof net.minecraft.server.ChunkPosition) { if (MinecraftReflection.isChunkPosition(value)) {
EquivalentConverter<ChunkPosition> converter = ChunkPosition.getConverter(); EquivalentConverter<ChunkPosition> converter = ChunkPosition.getConverter();
return converter.getGeneric(net.minecraft.server.ChunkPosition.class, converter.getSpecific(value)); return converter.getGeneric(MinecraftReflection.getChunkPositionClass(), converter.getSpecific(value));
} else if (value instanceof ItemStack) { } else if (MinecraftReflection.isItemStack(value)) {
return ((ItemStack) value).cloneItemStack(); return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(value).clone());
} else { } else {
// A string or primitive wrapper, which are all immutable. // A string or primitive wrapper, which are all immutable.
return value; return value;
} }
} }
@Override
public boolean equals(Object obj) {
// Quick checks
if (obj == this)
return true;
if (obj == null)
return false;
if (obj instanceof WrappedWatchableObject) {
WrappedWatchableObject other = (WrappedWatchableObject) obj;
return Objects.equal(getIndex(), other.getIndex()) &&
Objects.equal(getTypeID(), other.getTypeID()) &&
Objects.equal(getValue(), other.getValue());
}
// No, this is not equivalent
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(getIndex(), getTypeID(), getValue());
}
@Override
public String toString() {
return String.format("[%s: %s (%s)]", getIndex(), getValue(), getType().getSimpleName());
}
} }

View File

@ -12,4 +12,7 @@ global:
metrics: true metrics: true
# Automatically compile structure modifiers # Automatically compile structure modifiers
background compiler: true background compiler: true
# Disable version checking for the given Minecraft version. Backup your world first!
ignore version check:

View File

@ -1,9 +1,9 @@
name: ProtocolLib name: ProtocolLib
version: 1.7.1 version: 1.8.0
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
load: startup
main: com.comphenix.protocol.ProtocolLibrary main: com.comphenix.protocol.ProtocolLibrary
database: false database: false

View File

@ -0,0 +1,27 @@
package com.comphenix.protocol;
import static org.junit.Assert.*;
import org.junit.Test;
public class MinecraftVersionTest {
@Test
public void testComparision() {
MinecraftVersion within = new MinecraftVersion(1, 2, 5);
MinecraftVersion outside = new MinecraftVersion(1, 7, 0);
MinecraftVersion lower = new MinecraftVersion(1, 0, 0);
MinecraftVersion highest = new MinecraftVersion(1, 4, 5);
// Make sure this is valid
assertTrue(lower.compareTo(within) < 0 && within.compareTo(highest) < 0);
assertFalse(outside.compareTo(within) < 0 && outside.compareTo(highest) < 0);
}
public void testParsing() {
assertEquals(MinecraftVersion.extractVersion("CraftBukkit R3.0 (MC: 1.4.3)"), "1.4.3");
assertEquals(MinecraftVersion.extractVersion("CraftBukkit Test Beta 1 (MC: 1.10.01 )"), "1.10.01");
assertEquals(MinecraftVersion.extractVersion("Hello (MC: 2.3.4 ) "), "2.3.4");
}
}

View File

@ -1,26 +0,0 @@
package com.comphenix.protocol.reflect;
import static org.junit.Assert.*;
import net.minecraft.server.Packet103SetSlot;
import org.junit.Test;
import com.avaje.ebeaninternal.server.cluster.Packet;
import com.comphenix.protocol.reflect.StructureModifier;
public class StructureModifierTest {
@Test
public void test() throws FieldAccessException {
Packet103SetSlot move = new Packet103SetSlot();
StructureModifier<Object> modifier = new StructureModifier<Object>(
Packet103SetSlot.class, Packet.class, true);
move.a = 1;
int value = (Integer) modifier.withTarget(move).withType(int.class).read(0);
assertEquals(1, value);
}
}