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

View File

@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>1.7.1</version>
<version>1.8.0</version>
<packaging>jar</packaging>
<description>Provides read/write access to the Minecraft protocol.</description>
@ -202,7 +202,7 @@
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>1.3.2-R1.0</version>
<version>1.4.5-R0.3-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>

View File

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

View File

@ -47,6 +47,7 @@ class CommandProtocol extends CommandBase {
return true;
}
@SuppressWarnings("deprecation")
public void checkVersion(final CommandSender sender) {
// Perform on an async thread
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() {
@ -64,6 +65,7 @@ class CommandProtocol extends CommandBase {
updateFinished();
}
@SuppressWarnings("deprecation")
public void updateVersion(final CommandSender sender) {
// Perform on an async thread
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 IGNORE_VERSION_CHECK = "ignore version check";
private static final String BACKGROUND_COMPILER_ENABLED = "background compiler";
private static final String UPDATER_NOTIFY = "notify";
@ -149,6 +151,26 @@ class ProtocolConfig {
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.
* @return TRUE if metrics is enabled, FALSE otherwise.

View File

@ -19,6 +19,7 @@ package com.comphenix.protocol;
import java.io.IOException;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
@ -43,6 +44,15 @@ import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
* @author Kristian
*/
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.
@ -120,7 +130,7 @@ public class ProtocolLibrary extends JavaPlugin {
commandPacket = new CommandPacket(detailedReporter, this, logger, protocolManager);
// Send logging information to player listeners too
broadcastUsers(PERMISSION_INFO);
setupBroadcastUsers(PERMISSION_INFO);
} catch (Throwable e) {
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
if (redirectHandler != null)
return;
@ -151,7 +161,10 @@ public class ProtocolLibrary extends JavaPlugin {
redirectHandler = new Handler() {
@Override
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
@ -188,13 +201,13 @@ public class ProtocolLibrary extends JavaPlugin {
logger.info("Structure compiler thread has been disabled.");
}
// Handle unexpected Minecraft versions
verifyMinecraftVersion();
// Set up command handlers
registerCommand(CommandProtocol.NAME, commandProtocol);
registerCommand(CommandPacket.NAME, commandPacket);
// Notify server managers of incompatible plugins
checkForIncompatibility(manager);
// Player login and logout events
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) {
try {
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
public void onDisable() {
// Disable compiler

View File

@ -242,7 +242,7 @@ public class AsyncListenerHandler {
final AsyncRunnable listenerLoop = getListenerLoop();
filterManager.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), new Runnable() {
scheduleAsync(new Runnable() {
@Override
public void run() {
Thread thread = Thread.currentThread();
@ -290,7 +290,7 @@ public class AsyncListenerHandler {
final AsyncRunnable listenerLoop = getListenerLoop();
final Function<AsyncRunnable, Void> delegateCopy = executor;
filterManager.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), new Runnable() {
scheduleAsync(new Runnable() {
@Override
public void run() {
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:
* <p><code>

View File

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

View File

@ -95,6 +95,21 @@ public class DetailedErrorReporter implements ErrorReporter {
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
public void reportMinimal(Plugin sender, String methodName, Throwable error) {
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);
/**
* 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.
* @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.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import org.bukkit.World;
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.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import net.minecraft.server.Packet;
import com.google.common.collect.Maps;
/**
* Represents a Minecraft packet indirectly.
@ -59,14 +60,14 @@ public class PacketContainer implements Serializable {
private static final long serialVersionUID = 2074805748222377230L;
protected int id;
protected transient Packet handle;
protected transient Object handle;
// Current structure modifier
protected transient StructureModifier<Object> structureModifier;
// Support for serialization
private static Method writeMethod;
private static Method readMethod;
private static ConcurrentMap<Class<?>, Method> writeMethods = Maps.newConcurrentMap();
private static ConcurrentMap<Class<?>, Method> readMethods = Maps.newConcurrentMap();
/**
* Creates a packet container for a new packet.
@ -81,7 +82,7 @@ public class PacketContainer implements Serializable {
* @param id - ID of the given 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));
}
@ -91,7 +92,7 @@ public class PacketContainer implements Serializable {
* @param handle - contained packet.
* @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)
throw new IllegalArgumentException("handle cannot be null.");
@ -100,11 +101,17 @@ public class PacketContainer implements Serializable {
this.structureModifier = structure;
}
/**
* For serialization.
*/
protected PacketContainer() {
}
/**
* Retrieves the underlying Minecraft packet.
* @return Underlying Minecraft packet.
*/
public Packet getHandle() {
public Object getHandle() {
return handle;
}
@ -214,7 +221,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<ItemStack> getItemModifier() {
// Convert to and from the Bukkit wrapper
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
return structureModifier.<ItemStack[]>withType(
net.minecraft.server.ItemStack[].class,
MinecraftReflection.getItemStackArrayClass(),
BukkitConverters.getIgnoreNull(new EquivalentConverter<ItemStack[]>() {
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
for (int i = 0; i < result.length; i++) {
result[i] = (net.minecraft.server.ItemStack) stackConverter.getGeneric(
net.minecraft.server.ItemStack.class, specific[i]);
result[i] = stackConverter.getGeneric(
MinecraftReflection.getItemStackClass(), specific[i]);
}
return result;
}
@Override
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];
// Add the wrapper
@ -273,7 +280,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<WorldType> getWorldTypeModifier() {
// Convert to and from the Bukkit wrapper
return structureModifier.<WorldType>withType(
net.minecraft.server.WorldType.class,
MinecraftReflection.getWorldTypeClass(),
BukkitConverters.getWorldTypeConverter());
}
@ -284,7 +291,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<WrappedDataWatcher> getDataWatcherModifier() {
// Convert to and from the Bukkit wrapper
return structureModifier.<WrappedDataWatcher>withType(
net.minecraft.server.DataWatcher.class,
MinecraftReflection.getDataWatcherClass(),
BukkitConverters.getDataWatcherConverter());
}
@ -311,7 +318,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<ChunkPosition> getPositionModifier() {
// Convert to and from the Bukkit wrapper
return structureModifier.withType(
net.minecraft.server.ChunkPosition.class,
MinecraftReflection.getChunkPositionClass(),
ChunkPosition.getConverter());
}
@ -327,7 +334,7 @@ public class PacketContainer implements Serializable {
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(
net.minecraft.server.ChunkPosition.class,
MinecraftReflection.getChunkPositionClass(),
ChunkPosition.getConverter())
);
}
@ -344,7 +351,7 @@ public class PacketContainer implements Serializable {
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(
net.minecraft.server.WatchableObject.class,
MinecraftReflection.getWatchableObjectClass(),
BukkitConverters.getWatchableObjectConverter())
);
}
@ -400,13 +407,11 @@ public class PacketContainer implements Serializable {
// We'll take care of NULL packets as well
output.writeBoolean(handle != null);
// Retrieve the write method by reflection
if (writeMethod == null)
writeMethod = FuzzyReflection.fromObject(handle).getMethodByParameters("write", DataOutputStream.class);
try {
// 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) {
throw new IOException("Minecraft packet doesn't support DataOutputStream", e);
} catch (IllegalAccessException e) {
@ -429,13 +434,11 @@ public class PacketContainer implements Serializable {
// Create a default instance of the packet
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
try {
readMethod.invoke(handle, new DataInputStream(input));
getMethodLazily(readMethods, handle.getClass(), "read", DataInputStream.class).
invoke(handle, new DataInputStream(input));
} catch (IllegalArgumentException e) {
throw new IOException("Minecraft packet doesn't support DataInputStream", e);
} catch (IllegalAccessException e) {
@ -448,4 +451,30 @@ public class PacketContainer implements Serializable {
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.Set;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.EntityTrackerEntry;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Lists;
/**
@ -51,6 +48,7 @@ class EntityUtilities {
private static Field entityTrackerField;
private static Field trackedEntitiesField;
private static Field trackedPlayersField;
private static Field trackerField;
private static Method hashGetMethod;
private static Method scanPlayersMethod;
@ -142,9 +140,8 @@ class EntityUtilities {
// Wrap every player - we also ensure that the underlying tracker list is immutable
for (Object tracker : trackedPlayers) {
if (tracker instanceof EntityPlayer) {
EntityPlayer nmsPlayer = (EntityPlayer) tracker;
result.add(nmsPlayer.getBukkitEntity());
if (MinecraftReflection.isMinecraftPlayer(tracker)) {
result.add((Player) MinecraftReflection.getBukkitEntity(tracker));
}
}
return result;
@ -164,7 +161,8 @@ class EntityUtilities {
* @throws FieldAccessException
*/
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.
if (entityTrackerField == null)
@ -193,7 +191,7 @@ class EntityUtilities {
// The Minecraft field that's NOT filled in by the constructor
trackedEntitiesField = FuzzyReflection.fromObject(tracker, true).
getFieldByType(FuzzyReflection.MINECRAFT_OBJECT, ignoredTypes);
getFieldByType(MinecraftReflection.MINECRAFT_OBJECT, ignoredTypes);
}
// Read the entity hashmap
@ -247,17 +245,24 @@ class EntityUtilities {
*/
public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException {
try {
EntityTrackerEntry trackerEntry = (EntityTrackerEntry) getEntityTrackerEntry(world, entityID);
Object trackerEntry = getEntityTrackerEntry(world, entityID);
Object tracker = null;
// Handle NULL cases
if (trackerEntry != null && trackerEntry.tracker != null) {
return trackerEntry.tracker.getBukkitEntity();
} else {
return null;
if (trackerEntry != null) {
if (trackerField == null)
trackerField = trackerEntry.getClass().getField("tracker");
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) {
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;
import net.minecraft.server.Packet;
import com.comphenix.protocol.events.PacketEvent;
/**
@ -45,7 +43,7 @@ public interface ListenerInvoker {
* @param packet - the packet.
* @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.

View File

@ -23,11 +23,12 @@ import java.util.List;
import java.util.Map;
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.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
@ -77,7 +78,7 @@ class MinecraftRegistry {
*/
private static FuzzyReflection getPacketRegistry() {
if (packetRegistry == null)
packetRegistry = FuzzyReflection.fromClass(Packet.class, true);
packetRegistry = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true);
return packetRegistry;
}
@ -166,7 +167,7 @@ class MinecraftRegistry {
// Optimized lookup
if (lookup.containsKey(packetID)) {
return lookup.get(packetID);
return removeEnhancer(lookup.get(packetID), forceVanilla);
}
// Will most likely not be used
@ -174,10 +175,27 @@ class MinecraftRegistry {
if (Objects.equal(entry.getValue(), packetID)) {
// Attempt to get the vanilla class here too
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.");
}
/**
* 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.util.List;
import net.minecraft.server.Packet;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.FieldAccessException;
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);
} catch (IllegalArgumentException e) {

View File

@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
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.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
@ -508,7 +508,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
if (packetCreation.compareAndSet(false, true))
incrementPhases(GamePhase.PLAYING);
Packet mcPacket = packet.getHandle();
Object mcPacket = packet.getHandle();
// Make sure the packet isn't cancelled
packetInjector.undoCancel(packet.getID(), mcPacket);
@ -686,9 +686,11 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
}
@Override
public int getPacketID(Packet packet) {
public int getPacketID(Object packet) {
if (packet == 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());
}

View File

@ -27,7 +27,6 @@ import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.entity.Player;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Callback;
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.reflect.FieldUtils;
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.
@ -80,7 +80,7 @@ class PacketInjector {
* @param id - the id of the packet.
* @param packet - packet to uncancel.
*/
public void undoCancel(Integer id, Packet packet) {
public void undoCancel(Integer id, Object packet) {
ReadPacketModifier modifier = readModifier.get(id);
// See if this packet has been cancelled before
@ -92,7 +92,8 @@ class PacketInjector {
private void initialize() throws IllegalAccessException {
if (intHashMap == null) {
// 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 {
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.PacketEvent;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
@ -61,7 +60,7 @@ class ReadPacketModifier implements MethodInterceptor {
* Remove any packet overrides.
* @param packet - the packet to rever
*/
public void removeOverride(Packet packet) {
public void removeOverride(Object packet) {
override.remove(packet);
}
@ -70,7 +69,7 @@ class ReadPacketModifier implements MethodInterceptor {
* @param packet - the given packet.
* @return Overriden object.
*/
public Object getOverride(Packet packet) {
public Object getOverride(Object packet) {
return override.get(packet);
}
@ -79,7 +78,7 @@ class ReadPacketModifier implements MethodInterceptor {
* @param packet - the packet to check.
* @return TRUE if it has been cancelled, FALSE otherwise.
*/
public boolean hasCancelled(Packet packet) {
public boolean hasCancelled(Object packet) {
return getOverride(packet) == CANCEL_MARKER;
}
@ -121,12 +120,12 @@ class ReadPacketModifier implements MethodInterceptor {
DataInputStream input = (DataInputStream) args[0];
// Let the people know
PacketContainer container = new PacketContainer(packetID, (Packet) thisObj);
PacketContainer container = new PacketContainer(packetID, thisObj);
PacketEvent event = packetInjector.packetRecieved(container, input);
// Handle override
if (event != null) {
Packet result = event.getPacket().getHandle();
Object result = event.getPacket().getHandle();
if (event.isCancelled()) {
override.put(thisObj, CANCEL_MARKER);

View File

@ -48,7 +48,8 @@ public final class SortedPacketListenerList extends AbstractConcurrentListenerMu
element.getListener().onPacketReceiving(event);
} catch (Throwable e) {
// 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);
} catch (Throwable e) {
// 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.ConcurrentMap;
import net.minecraft.server.Packet;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.reflect.compiler.CompileListener;
import com.comphenix.protocol.reflect.compiler.CompiledStructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Caches structure modifiers.
@ -45,9 +44,9 @@ public class StructureCache {
* @param id - packet ID.
* @return Created packet.
*/
public static Packet newPacket(int id) {
public static Object newPacket(int id) {
try {
return (Packet) MinecraftRegistry.getPacketClassFromID(id, true).newInstance();
return MinecraftRegistry.getPacketClassFromID(id, true).newInstance();
} catch (InstantiationException e) {
return null;
} catch (IllegalAccessException e) {
@ -79,7 +78,7 @@ public class StructureCache {
if (result == null) {
// Use the vanilla class definition
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);

View File

@ -25,7 +25,7 @@ import java.util.Set;
import com.comphenix.protocol.injector.ListenerInvoker;
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.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
@ -35,7 +35,7 @@ import net.sf.cglib.proxy.MethodProxy;
*
* @author Kristian
*/
class InjectedArrayList extends ArrayList<Packet> {
class InjectedArrayList extends ArrayList<Object> {
/**
* Silly Eclipse.
@ -43,19 +43,22 @@ class InjectedArrayList extends ArrayList<Packet> {
private static final long serialVersionUID = -1173865905404280990L;
private transient PlayerInjector injector;
private transient Set<Packet> ignoredPackets;
private transient Set<Object> ignoredPackets;
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.injector = injector;
this.ignoredPackets = ignoredPackets;
this.callback = new InvertedIntegerCallback();
}
@Override
public boolean add(Packet packet) {
public boolean add(Object packet) {
Packet result = null;
Object result = null;
// Check for fake packets and ignored packets
if (packet instanceof FakePacket) {
@ -90,12 +93,14 @@ class InjectedArrayList extends ArrayList<Packet> {
* @param source - packet to invert.
* @return The inverted packet.
*/
Packet createNegativePacket(Packet source) {
Object createNegativePacket(Object source) {
ListenerInvoker invoker = injector.getInvoker();
int packetID = invoker.getPacketID(source);
Class<?> type = invoker.getPacketClassFromID(packetID, true);
System.out.println(type.getName());
// We want to subtract the byte amount that were added to the running
// total of outstanding packets. Otherwise, cancelling too many packets
// might cause a "disconnect.overflow" error.
@ -122,15 +127,20 @@ class InjectedArrayList extends ArrayList<Packet> {
ex.setCallbackType(InvertedIntegerCallback.class);
Class<?> proxyClass = ex.createClass();
Enhancer.registerCallbacks(proxyClass, new Callback[] { callback });
// Temporarily associate the fake packet class
invoker.registerPacketClass(proxyClass, packetID);
try {
// Temporarily associate the fake packet class
invoker.registerPacketClass(proxyClass, packetID);
return proxyClass.newInstance();
Packet fake = (Packet) Enhancer.create(proxyClass, new InvertedIntegerCallback());
// Remove this association
invoker.unregisterPacketClass(proxyClass);
return fake;
} 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.List;
import net.minecraft.server.NetLoginHandler;
import net.sf.cglib.proxy.Factory;
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.ObjectCloner;
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.
@ -229,7 +229,7 @@ class InjectedServerConnection {
@Override
protected void onInserting(Object inserting) {
// Ready for some login handler injection?
if (inserting instanceof NetLoginHandler) {
if (MinecraftReflection.isLoginHandler(inserting)) {
Object replaced = netLoginInjector.onNetLoginCreated(inserting);
// Only replace if it has changed
@ -241,7 +241,7 @@ class InjectedServerConnection {
@Override
protected void onRemoved(Object removing) {
// Clean up?
if (removing instanceof NetLoginHandler) {
if (MinecraftReflection.isLoginHandler(removing)) {
netLoginInjector.cleanup(removing);
}
}

View File

@ -40,8 +40,6 @@ import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.VolatileField;
import com.google.common.collect.Sets;
import net.minecraft.server.Packet;
/**
* Injection hook that overrides the packet queue lists in NetworkHandler.
*
@ -58,7 +56,7 @@ class NetworkFieldInjector extends PlayerInjector {
}
// 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
private List<VolatileField> overridenLists = new ArrayList<VolatileField>();
@ -99,7 +97,7 @@ class NetworkFieldInjector extends PlayerInjector {
}
@Override
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException {
public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
if (networkManager != null) {
try {
@ -147,14 +145,14 @@ class NetworkFieldInjector extends PlayerInjector {
VolatileField overwriter = new VolatileField(field, networkManager, true);
@SuppressWarnings("unchecked")
List<Packet> minecraftList = (List<Packet>) overwriter.getOldValue();
List<Object> minecraftList = (List<Object>) overwriter.getOldValue();
synchronized(syncObject) {
// 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
for (Packet packet : minecraftList) {
for (Object packet : minecraftList) {
hackedList.add(packet);
}
@ -172,8 +170,8 @@ class NetworkFieldInjector extends PlayerInjector {
protected void cleanHook() {
// Clean up
for (VolatileField overriden : overridenLists) {
List<Packet> minecraftList = (List<Packet>) overriden.getOldValue();
List<Packet> hacketList = (List<Packet>) overriden.getValue();
List<Object> minecraftList = (List<Object>) overriden.getOldValue();
List<Object> hacketList = (List<Object>) overriden.getValue();
if (minecraftList == hacketList) {
return;
@ -183,7 +181,7 @@ class NetworkFieldInjector extends PlayerInjector {
synchronized(syncObject) {
try {
// Copy over current packets
for (Packet packet : (List<Packet>) overriden.getValue()) {
for (Object packet : (List<Object>) overriden.getValue()) {
minecraftList.add(packet);
}
} finally {

View File

@ -19,7 +19,6 @@ package com.comphenix.protocol.injector.player;
import java.lang.reflect.InvocationTargetException;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
@ -67,7 +66,7 @@ class NetworkObjectInjector extends PlayerInjector {
}
@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();
if (networkDelegate != null) {
@ -114,7 +113,7 @@ class NetworkObjectInjector extends PlayerInjector {
Callback queueFilter = new MethodInterceptor() {
@Override
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) {
packet = handlePacketSending(packet);

View File

@ -21,7 +21,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
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.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.ExistingGenerator;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Represents a player hook into the NetServerHandler class.
@ -94,7 +94,7 @@ public class NetworkServerInjector extends PlayerInjector {
}
@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();
if (serverDeleage != null) {
@ -152,8 +152,7 @@ public class NetworkServerInjector extends PlayerInjector {
Callback sendPacketCallback = new MethodInterceptor() {
@Override
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) {
packet = handlePacketSending(packet);
@ -237,7 +236,7 @@ public class NetworkServerInjector extends PlayerInjector {
}
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
if (clazz.getName().startsWith("net.minecraft.server."))
if (clazz.getName().startsWith(MinecraftReflection.getMinecraftPackage()))
return clazz;
else if (clazz.equals(Object.class))
return clazz;

View File

@ -26,8 +26,6 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import net.minecraft.server.Packet;
import org.bukkit.Server;
import org.bukkit.entity.Player;
@ -501,7 +499,7 @@ public class PlayerInjectionHandler {
* @throws IllegalAccessException If the reflection machinery failed.
* @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);

View File

@ -25,12 +25,8 @@ import java.lang.reflect.Method;
import java.net.Socket;
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 org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
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.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker;
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.StructureModifier;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.utility.MinecraftReflection;
abstract class PlayerInjector {
@ -122,9 +120,9 @@ abstract class PlayerInjector {
* @param player - the player to retrieve.
* @return Notch player object.
*/
protected EntityPlayer getEntityPlayer(Player player) {
CraftPlayer craft = (CraftPlayer) player;
return craft.getHandle();
protected Object getEntityPlayer(Player player) {
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
return unwrapper.unwrapItem(player);
}
/**
@ -138,7 +136,7 @@ abstract class PlayerInjector {
//Dispatch to the correct injection method
if (injectionSource instanceof Player)
initializePlayer(injectionSource);
else if (injectionSource instanceof NetLoginHandler)
else if (MinecraftReflection.isLoginHandler(injectionSource))
initializeLogin(injectionSource);
else
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) {
EntityPlayer notchEntity = getEntityPlayer((Player) player);
Object notchEntity = getEntityPlayer((Player) player);
if (!hasInitialized) {
// Do this first, in case we encounter an exception
@ -204,7 +202,7 @@ abstract class PlayerInjector {
// And the queue method
if (queueMethod == null)
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
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 {
Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true);
@ -379,7 +377,7 @@ abstract class PlayerInjector {
try {
// Well, that sucks. Try just Minecraft objects then.
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
getFieldByType(FuzzyReflection.MINECRAFT_OBJECT);
getFieldByType(MinecraftReflection.MINECRAFT_OBJECT);
} catch (RuntimeException e2) {
throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage());
@ -398,10 +396,10 @@ abstract class PlayerInjector {
* @return The stored entity player.
* @throws IllegalAccessException If the reflection failed.
*/
private EntityPlayer getEntityPlayer(Object netHandler) throws IllegalAccessException {
private Object getEntityPlayer(Object netHandler) throws IllegalAccessException {
if (entityPlayerField == null)
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 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();
// Get the process method
if (processMethod == null) {
try {
processMethod = FuzzyReflection.fromClass(Packet.class).
getMethodByParameters("processPacket", netHandlerField.getType());
processMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
getMethodByParameters("processPacket", netHandlerField.getType());
} catch (RuntimeException e) {
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 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.
@ -501,7 +499,7 @@ abstract class PlayerInjector {
* @param packet - packet to sent.
* @return The given packet, or the packet replaced by the listeners.
*/
public Packet handlePacketSending(Packet packet) {
public Object handlePacketSending(Object packet) {
try {
// Get the packet ID too
Integer id = invoker.getPacketID(packet);
@ -516,7 +514,7 @@ abstract class PlayerInjector {
if (updateOnLogin) {
if (id == Packets.Server.LOGIN) {
try {
updatedPlayer = getEntityPlayer(getNetHandler()).getBukkitEntity();
updatedPlayer = (Player) MinecraftReflection.getBukkitEntity(getEntityPlayer(getNetHandler()));
} catch (IllegalAccessException e) {
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.
*/
public boolean start() {
@SuppressWarnings("deprecation")
public boolean start() {
synchronized (optOutLock) {
// Did we opt out?
if (isOptOut()) {

View File

@ -33,11 +33,6 @@ import java.util.regex.Pattern;
*/
public class FuzzyReflection {
/**
* Matches a Minecraft object.
*/
public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+";
// The class we're actually representing
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;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import net.minecraft.server.DataWatcher;
import net.minecraft.server.WatchableObject;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.Entity;
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.FieldAccessException;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Contains several useful equivalent converters for normal Bukkit types.
@ -29,9 +27,13 @@ public class BukkitConverters {
// Check whether or not certain classes exists
private static boolean hasWorldType = false;
// Used to access the world type
private static Method worldTypeName;
private static Method worldTypeGetType;
static {
try {
Class.forName("net.minecraft.server.WorldType");
Class.forName(MinecraftReflection.getMinecraftPackage() + ".WorldType");
hasWorldType = true;
} catch (ClassNotFoundException e) {
}
@ -100,8 +102,8 @@ public class BukkitConverters {
}
public WrappedWatchableObject getSpecific(Object generic) {
if (generic instanceof WatchableObject)
return new WrappedWatchableObject((WatchableObject) generic);
if (MinecraftReflection.isWatchableObject(generic))
return new WrappedWatchableObject(generic);
else if (generic instanceof WrappedWatchableObject)
return (WrappedWatchableObject) generic;
else
@ -128,8 +130,8 @@ public class BukkitConverters {
@Override
public WrappedDataWatcher getSpecific(Object generic) {
if (generic instanceof DataWatcher)
return new WrappedDataWatcher((DataWatcher) generic);
if (MinecraftReflection.isDataWatcher(generic))
return new WrappedDataWatcher(generic);
else if (generic instanceof WrappedDataWatcher)
return (WrappedDataWatcher) generic;
else
@ -153,15 +155,35 @@ public class BukkitConverters {
return null;
return getIgnoreNull(new EquivalentConverter<WorldType>() {
@SuppressWarnings("unchecked")
@Override
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
public WorldType getSpecific(Object generic) {
net.minecraft.server.WorldType type = (net.minecraft.server.WorldType) generic;
return WorldType.getByName(type.name());
try {
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
@ -221,12 +243,12 @@ public class BukkitConverters {
public static EquivalentConverter<ItemStack> getItemStackConverter() {
return getIgnoreNull(new EquivalentConverter<ItemStack>() {
public Object getGeneric(Class<?> genericType, ItemStack specific) {
return toStackNMS(specific);
return MinecraftReflection.getMinecraftItemStack(specific);
}
@Override
public ItemStack getSpecific(Object generic) {
return new CraftItemStack((net.minecraft.server.ItemStack) generic);
return MinecraftReflection.getBukkitItemStack(generic);
}
@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.
* @param delegate - the underlying equivalent converter.

View File

@ -1,10 +1,13 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import org.bukkit.util.Vector;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
/**
@ -19,6 +22,8 @@ public class ChunkPosition {
*/
public static ChunkPosition ORIGIN = new ChunkPosition(0, 0, 0);
private static Constructor<?> chunkPositionConstructor;
// Use protected members, like Bukkit
protected final int x;
protected final int y;
@ -128,27 +133,35 @@ public class ChunkPosition {
*/
public static EquivalentConverter<ChunkPosition> getConverter() {
return new EquivalentConverter<ChunkPosition>() {
@SuppressWarnings("unchecked")
@Override
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
public ChunkPosition getSpecific(Object generic) {
if (generic instanceof net.minecraft.server.ChunkPosition) {
net.minecraft.server.ChunkPosition other = (net.minecraft.server.ChunkPosition) generic;
if (MinecraftReflection.isChunkPosition(generic)) {
// Use a structure modifier
intModifier = new StructureModifier<Object>(generic.getClass(), null, false).withType(int.class);
try {
if (intModifier == null)
return new ChunkPosition(other.x, other.y, other.z);
} 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.");
}
// Damn it all
if (intModifier.size() < 3) {
throw new IllegalStateException("Cannot read class " + generic.getClass() + " for its integer fields.");
}
if (intModifier.size() >= 3) {

View File

@ -1,9 +1,9 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
import net.minecraft.server.ChunkCoordinates;
/**
* Allows access to a chunk coordinate.
*
@ -16,23 +16,42 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
*/
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.
*/
@SuppressWarnings("rawtypes")
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.
* @param handle - the NMS chunk coordinates.
*/
public WrappedChunkCoordinate(ChunkCoordinates handle) {
@SuppressWarnings("rawtypes")
public WrappedChunkCoordinate(Comparable handle) {
if (handle == null)
throw new IllegalArgumentException("handle cannot be NULL");
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());
}
public ChunkCoordinates getHandle() {
public Object getHandle() {
return handle;
}
@ -65,7 +84,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
* @return The x coordinate.
*/
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.
*/
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.
*/
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.
*/
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.
*/
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());
}
/**
* Set the z coordinate of the underlying coordiate.
* @param newZ - the new z coordinate.
*/
public void setZ(int newZ) {
handle.z = newZ;
}
@SuppressWarnings("unchecked")
@Override
public int compareTo(WrappedChunkCoordinate other) {
// 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.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.ReentrantReadWriteLock;
import javax.annotation.Nullable;
import org.bukkit.entity.Entity;
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.FieldUtils;
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 net.minecraft.server.DataWatcher;
import net.minecraft.server.WatchableObject;
import com.google.common.collect.Iterators;
/**
* Wraps a DataWatcher that is used to transmit arbitrary key-value pairs with a given entity.
*
* @author Kristian
*/
public class WrappedDataWatcher {
public class WrappedDataWatcher implements Iterable<WrappedWatchableObject> {
/**
* Used to assign integer IDs to given types.
@ -55,7 +58,7 @@ public class WrappedDataWatcher {
private static boolean hasInitialized;
// The underlying DataWatcher we're modifying
protected DataWatcher handle;
protected Object handle;
// Lock
private ReadWriteLock readWriteLock;
@ -69,7 +72,13 @@ public class WrappedDataWatcher {
*/
public WrappedDataWatcher() {
// 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.
* @throws FieldAccessException If we're unable to wrap a DataWatcher.
*/
public WrappedDataWatcher(DataWatcher handle) {
this.handle = handle;
public WrappedDataWatcher(Object 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 {
initialize();
} catch (FieldAccessException e) {
throw new RuntimeException("Cannot initialize wrapper.", e);
}
this.handle = handle;
initialize();
}
/**
* 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.
* @throws FieldAccessException Unable to read watchable objects.
*/
public WrappedDataWatcher(List<WrappedWatchableObject> watchableObjects) throws FieldAccessException {
this();
// Fill the underlying map
for (WrappedWatchableObject watched : watchableObjects) {
setObject(watched.getIndex(), watched.getValue());
Lock writeLock = getReadWriteLock().writeLock();
Map<Integer, Object> map = getWatchableObjectMap();
writeLock.lock();
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.
* @return The underlying data watcher.
*/
public DataWatcher getHandle() {
public Object getHandle() {
return handle;
}
@ -215,7 +239,7 @@ public class WrappedDataWatcher {
*/
public Object getObject(int index) throws FieldAccessException {
// The get method will take care of concurrency
WatchableObject watchable = getWatchedObject(index);
Object watchable = getWatchedObject(index);
if (watchable != null) {
return new WrappedWatchableObject(watchable).getValue();
@ -230,15 +254,16 @@ public class WrappedDataWatcher {
* @throws FieldAccessException If reflection failed.
*/
public List<WrappedWatchableObject> getWatchableObjects() throws FieldAccessException {
try {
getReadWriteLock().readLock().lock();
Lock readLock = getReadWriteLock().readLock();
readLock.lock();
try {
List<WrappedWatchableObject> result = new ArrayList<WrappedWatchableObject>();
// Add each watchable object to the list
for (Object watchable : getWatchableObjectMap().values()) {
if (watchable != null) {
result.add(new WrappedWatchableObject((WatchableObject) watchable));
result.add(new WrappedWatchableObject(watchable));
} else {
result.add(null);
}
@ -246,7 +271,7 @@ public class WrappedDataWatcher {
return result;
} 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.
* @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.
* @param index - index of the watched byte.
@ -305,7 +361,7 @@ public class WrappedDataWatcher {
writeLock.lock();
try {
WatchableObject watchable = getWatchedObject(index);
Object watchable = getWatchedObject(index);
if (watchable != null) {
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
if (getKeyValueMethod != null) {
try {
return (WatchableObject) getKeyValueMethod.invoke(handle, index);
return getKeyValueMethod.invoke(handle, index);
} catch (Exception e) {
throw new FieldAccessException("Cannot invoke get key method for index " + index, e);
}
} else {
try {
getReadWriteLock().readLock().lock();
return (WatchableObject) getWatchableObjectMap().get(index);
return getWatchableObjectMap().get(index);
} finally {
getReadWriteLock().readLock().unlock();
@ -388,8 +444,8 @@ public class WrappedDataWatcher {
*/
public static WrappedDataWatcher getEntityWatcher(Entity entity) throws FieldAccessException {
if (entityDataField == null)
entityDataField = FuzzyReflection.fromClass(net.minecraft.server.Entity.class, true).
getFieldByType("datawatcher", DataWatcher.class);
entityDataField = FuzzyReflection.fromClass(MinecraftReflection.getEntityClass(), true).
getFieldByType("datawatcher", MinecraftReflection.getDataWatcherClass());
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
@ -397,7 +453,7 @@ public class WrappedDataWatcher {
Object nsmWatcher = FieldUtils.readField(entityDataField, unwrapper.unwrapItem(entity), true);
if (nsmWatcher != null)
return new WrappedDataWatcher((DataWatcher) nsmWatcher);
return new WrappedDataWatcher(nsmWatcher);
else
return null;
@ -417,7 +473,7 @@ public class WrappedDataWatcher {
else
return;
FuzzyReflection fuzzy = FuzzyReflection.fromClass(DataWatcher.class, true);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getDataWatcherClass(), true);
for (Field lookup : fuzzy.getFieldListByType(Map.class)) {
if (Modifier.isStatic(lookup.getModifiers())) {
@ -476,4 +532,20 @@ public class WrappedDataWatcher {
// 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;
import java.lang.reflect.Constructor;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import net.minecraft.server.ChunkCoordinates;
import net.minecraft.server.ItemStack;
import net.minecraft.server.WatchableObject;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
/**
* Represents a watchable object.
@ -22,7 +24,13 @@ public class WrappedWatchableObject {
// The field containing the value itself
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;
// Type of the stored value
@ -32,7 +40,7 @@ public class WrappedWatchableObject {
* Wrap a given raw Minecraft watchable object.
* @param handle - the raw watchable object to wrap.
*/
public WrappedWatchableObject(WatchableObject handle) {
public WrappedWatchableObject(Object handle) {
load(handle);
}
@ -41,6 +49,7 @@ public class WrappedWatchableObject {
* @param index - the index.
* @param value - non-null value of specific types.
*/
@SuppressWarnings("unchecked")
public WrappedWatchableObject(int index, Object value) {
if (value == null)
throw new IllegalArgumentException("Value cannot be NULL.");
@ -49,24 +58,44 @@ public class WrappedWatchableObject {
Integer typeID = WrappedDataWatcher.getTypeID(value.getClass());
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 {
throw new IllegalArgumentException("Cannot watch the type " + value.getClass());
}
}
// Wrap a NMS object
private void load(WatchableObject handle) {
private void load(Object handle) {
initialize();
this.handle = 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.
* @return The underlying watchable object.
*/
public WatchableObject getHandle() {
public Object getHandle() {
return handle;
}
@ -76,7 +105,8 @@ public class WrappedWatchableObject {
private static void initialize() {
if (!hasInitialized) {
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.
* @return The wrapped object.
*/
@SuppressWarnings("rawtypes")
static Object getWrapped(Object value) {
// Handle the special cases
if (value instanceof net.minecraft.server.ItemStack) {
if (MinecraftReflection.isItemStack(value)) {
return BukkitConverters.getItemStackConverter().getSpecific(value);
} else if (value instanceof ChunkCoordinates) {
return new WrappedChunkCoordinate((ChunkCoordinates) value);
} else if (MinecraftReflection.isChunkCoordinates(value)) {
return new WrappedChunkCoordinate((Comparable) value);
} else {
return value;
}
@ -221,9 +252,9 @@ public class WrappedWatchableObject {
* @return The wrapped class type.
*/
static Class<?> getWrappedType(Class<?> unwrapped) {
if (unwrapped.equals(net.minecraft.server.ChunkPosition.class))
if (unwrapped.equals(MinecraftReflection.getChunkPositionClass()))
return ChunkPosition.class;
else if (unwrapped.equals(ChunkCoordinates.class))
else if (unwrapped.equals(MinecraftReflection.getChunkCoordinatesClass()))
return WrappedChunkCoordinate.class;
else
return unwrapped;
@ -240,7 +271,7 @@ public class WrappedWatchableObject {
return ((WrappedChunkCoordinate) wrapped).getHandle();
else if (wrapped instanceof ItemStack)
return BukkitConverters.getItemStackConverter().getGeneric(
net.minecraft.server.ItemStack.class, (org.bukkit.inventory.ItemStack) wrapped);
MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
else
return wrapped;
}
@ -252,9 +283,9 @@ public class WrappedWatchableObject {
*/
static Class<?> getUnwrappedType(Class<?> wrapped) {
if (wrapped.equals(ChunkPosition.class))
return net.minecraft.server.ChunkPosition.class;
return MinecraftReflection.getChunkPositionClass();
else if (wrapped.equals(WrappedChunkCoordinate.class))
return ChunkCoordinates.class;
return MinecraftReflection.getChunkCoordinatesClass();
else
return wrapped;
}
@ -265,7 +296,8 @@ public class WrappedWatchableObject {
* @throws FieldAccessException If we're unable to use reflection.
*/
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.setIndex(getIndex());
@ -275,18 +307,48 @@ public class WrappedWatchableObject {
}
// Helper
private Object getClonedValue() throws FieldAccessException {
Object getClonedValue() throws FieldAccessException {
Object value = getValue();
// 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();
return converter.getGeneric(net.minecraft.server.ChunkPosition.class, converter.getSpecific(value));
} else if (value instanceof ItemStack) {
return ((ItemStack) value).cloneItemStack();
return converter.getGeneric(MinecraftReflection.getChunkPositionClass(), converter.getSpecific(value));
} else if (MinecraftReflection.isItemStack(value)) {
return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(value).clone());
} else {
// A string or primitive wrapper, which are all immutable.
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

@ -13,3 +13,6 @@ global:
# Automatically compile structure modifiers
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
version: 1.7.1
version: 1.8.0
description: Provides read/write access to the Minecraft protocol.
author: Comphenix
website: http://www.comphenix.net/ProtocolLib
load: startup
main: com.comphenix.protocol.ProtocolLibrary
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);
}
}