Massive update that ensures ProtocolLib is compatible with 1.4.5-R3.0

Due to 8f12382e8efc8c39a919af9180dd884caf3720ff, CraftBukkit has
now moved the net.minecraft.server package and org.bukkit.craftbukkit
to versioned package names, breaking all plugins that rely on these
directly.

ProtocolLib was never intended to be updated specifcally for new
versions of Minecraft (hence the index-based system of accessing
packets instead of making wrapper classes), so we will use reflection
to "ignore" this hack entirely. Luckily, ProtocolLib was initially 
designed for this in mind, so we don't have to refactor it entirely.
This commit is contained in:
Kristian S. Stangeland 2012-12-06 03:37:20 +01:00
parent 45c28d47c8
commit 0745fba881
27 changed files with 757 additions and 243 deletions

View File

@ -139,7 +139,7 @@
<dependency> <dependency>
<groupId>org.bukkit</groupId> <groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId> <artifactId>craftbukkit</artifactId>
<version>1.3.2-R1.0</version> <version>1.4.5-R0.3-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

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

View File

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

View File

@ -40,14 +40,13 @@ import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedWatchableObject; import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import net.minecraft.server.Packet;
/** /**
* Represents a Minecraft packet indirectly. * Represents a Minecraft packet indirectly.
* *
@ -61,7 +60,7 @@ public class PacketContainer implements Serializable {
private static final long serialVersionUID = 2074805748222377230L; private static final long serialVersionUID = 2074805748222377230L;
protected int id; protected int id;
protected transient Packet handle; protected transient Object handle;
// Current structure modifier // Current structure modifier
protected transient StructureModifier<Object> structureModifier; protected transient StructureModifier<Object> structureModifier;
@ -83,7 +82,7 @@ public class PacketContainer implements Serializable {
* @param id - ID of the given packet. * @param id - ID of the given packet.
* @param handle - contained packet. * @param handle - contained packet.
*/ */
public PacketContainer(int id, Packet handle) { public PacketContainer(int id, Object handle) {
this(id, handle, StructureCache.getStructure(id).withTarget(handle)); this(id, handle, StructureCache.getStructure(id).withTarget(handle));
} }
@ -93,7 +92,7 @@ public class PacketContainer implements Serializable {
* @param handle - contained packet. * @param handle - contained packet.
* @param structure - structure modifier. * @param structure - structure modifier.
*/ */
public PacketContainer(int id, Packet handle, StructureModifier<Object> structure) { public PacketContainer(int id, Object handle, StructureModifier<Object> structure) {
if (handle == null) if (handle == null)
throw new IllegalArgumentException("handle cannot be null."); throw new IllegalArgumentException("handle cannot be null.");
@ -112,7 +111,7 @@ public class PacketContainer implements Serializable {
* Retrieves the underlying Minecraft packet. * Retrieves the underlying Minecraft packet.
* @return Underlying Minecraft packet. * @return Underlying Minecraft packet.
*/ */
public Packet getHandle() { public Object getHandle() {
return handle; return handle;
} }
@ -222,7 +221,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<ItemStack> getItemModifier() { public StructureModifier<ItemStack> getItemModifier() {
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.<ItemStack>withType( return structureModifier.<ItemStack>withType(
net.minecraft.server.ItemStack.class, BukkitConverters.getItemStackConverter()); MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter());
} }
/** /**
@ -238,23 +237,23 @@ public class PacketContainer implements Serializable {
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.<ItemStack[]>withType( return structureModifier.<ItemStack[]>withType(
net.minecraft.server.ItemStack[].class, MinecraftReflection.getItemStackArrayClass(),
BukkitConverters.getIgnoreNull(new EquivalentConverter<ItemStack[]>() { BukkitConverters.getIgnoreNull(new EquivalentConverter<ItemStack[]>() {
public Object getGeneric(Class<?>genericType, ItemStack[] specific) { public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
net.minecraft.server.ItemStack[] result = new net.minecraft.server.ItemStack[specific.length]; Object[] result = new Object[specific.length];
// Unwrap every item // Unwrap every item
for (int i = 0; i < result.length; i++) { for (int i = 0; i < result.length; i++) {
result[i] = (net.minecraft.server.ItemStack) stackConverter.getGeneric( result[i] = stackConverter.getGeneric(
net.minecraft.server.ItemStack.class, specific[i]); MinecraftReflection.getItemStackClass(), specific[i]);
} }
return result; return result;
} }
@Override @Override
public ItemStack[] getSpecific(Object generic) { public ItemStack[] getSpecific(Object generic) {
net.minecraft.server.ItemStack[] input = (net.minecraft.server.ItemStack[]) generic; Object[] input = (Object[]) generic;
ItemStack[] result = new ItemStack[input.length]; ItemStack[] result = new ItemStack[input.length];
// Add the wrapper // Add the wrapper
@ -281,7 +280,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<WorldType> getWorldTypeModifier() { public StructureModifier<WorldType> getWorldTypeModifier() {
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.<WorldType>withType( return structureModifier.<WorldType>withType(
net.minecraft.server.WorldType.class, MinecraftReflection.getWorldTypeClass(),
BukkitConverters.getWorldTypeConverter()); BukkitConverters.getWorldTypeConverter());
} }
@ -292,7 +291,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<WrappedDataWatcher> getDataWatcherModifier() { public StructureModifier<WrappedDataWatcher> getDataWatcherModifier() {
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.<WrappedDataWatcher>withType( return structureModifier.<WrappedDataWatcher>withType(
net.minecraft.server.DataWatcher.class, MinecraftReflection.getDataWatcherClass(),
BukkitConverters.getDataWatcherConverter()); BukkitConverters.getDataWatcherConverter());
} }
@ -319,7 +318,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<ChunkPosition> getPositionModifier() { public StructureModifier<ChunkPosition> getPositionModifier() {
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.withType( return structureModifier.withType(
net.minecraft.server.ChunkPosition.class, MinecraftReflection.getChunkPositionClass(),
ChunkPosition.getConverter()); ChunkPosition.getConverter());
} }
@ -335,7 +334,7 @@ public class PacketContainer implements Serializable {
return structureModifier.withType( return structureModifier.withType(
Collection.class, Collection.class,
BukkitConverters.getListConverter( BukkitConverters.getListConverter(
net.minecraft.server.ChunkPosition.class, MinecraftReflection.getChunkPositionClass(),
ChunkPosition.getConverter()) ChunkPosition.getConverter())
); );
} }
@ -352,7 +351,7 @@ public class PacketContainer implements Serializable {
return structureModifier.withType( return structureModifier.withType(
Collection.class, Collection.class,
BukkitConverters.getListConverter( BukkitConverters.getListConverter(
net.minecraft.server.WatchableObject.class, MinecraftReflection.getWatchableObjectClass(),
BukkitConverters.getWatchableObjectConverter()) BukkitConverters.getWatchableObjectConverter())
); );
} }

View File

@ -28,11 +28,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.EntityTrackerEntry;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -52,6 +48,7 @@ class EntityUtilities {
private static Field entityTrackerField; private static Field entityTrackerField;
private static Field trackedEntitiesField; private static Field trackedEntitiesField;
private static Field trackedPlayersField; private static Field trackedPlayersField;
private static Field trackerField;
private static Method hashGetMethod; private static Method hashGetMethod;
private static Method scanPlayersMethod; private static Method scanPlayersMethod;
@ -143,9 +140,8 @@ class EntityUtilities {
// Wrap every player - we also ensure that the underlying tracker list is immutable // Wrap every player - we also ensure that the underlying tracker list is immutable
for (Object tracker : trackedPlayers) { for (Object tracker : trackedPlayers) {
if (tracker instanceof EntityPlayer) { if (MinecraftReflection.isMinecraftPlayer(tracker)) {
EntityPlayer nmsPlayer = (EntityPlayer) tracker; result.add((Player) MinecraftReflection.getBukkitEntity(tracker));
result.add(nmsPlayer.getBukkitEntity());
} }
} }
return result; return result;
@ -165,7 +161,8 @@ class EntityUtilities {
* @throws FieldAccessException * @throws FieldAccessException
*/ */
private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Object worldServer = ((CraftWorld) world).getHandle(); BukkitUnwrapper unwrapper = new BukkitUnwrapper();
Object worldServer = unwrapper.unwrapItem(world);
// We have to rely on the class naming here. // We have to rely on the class naming here.
if (entityTrackerField == null) if (entityTrackerField == null)
@ -248,11 +245,15 @@ class EntityUtilities {
*/ */
public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException { public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException {
try { try {
EntityTrackerEntry trackerEntry = (EntityTrackerEntry) getEntityTrackerEntry(world, entityID); Object trackerEntry = getEntityTrackerEntry(world, entityID);
if (trackerField == null)
trackerField = trackerEntry.getClass().getField("tracker");
Object tracker = FieldUtils.readField(trackerField, trackerEntry, true);
// Handle NULL cases // Handle NULL cases
if (trackerEntry != null && trackerEntry.tracker != null) { if (trackerEntry != null && tracker != null) {
return trackerEntry.tracker.getBukkitEntity(); return (Entity) MinecraftReflection.getBukkitEntity(tracker);
} else { } else {
return null; return null;
} }

View File

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

View File

@ -23,12 +23,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.Factory;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
@ -78,7 +78,7 @@ class MinecraftRegistry {
*/ */
private static FuzzyReflection getPacketRegistry() { private static FuzzyReflection getPacketRegistry() {
if (packetRegistry == null) if (packetRegistry == null)
packetRegistry = FuzzyReflection.fromClass(Packet.class, true); packetRegistry = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true);
return packetRegistry; return packetRegistry;
} }

View File

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

View File

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

View File

@ -27,7 +27,6 @@ import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.Enhancer;
@ -81,7 +80,7 @@ class PacketInjector {
* @param id - the id of the packet. * @param id - the id of the packet.
* @param packet - packet to uncancel. * @param packet - packet to uncancel.
*/ */
public void undoCancel(Integer id, Packet packet) { public void undoCancel(Integer id, Object packet) {
ReadPacketModifier modifier = readModifier.get(id); ReadPacketModifier modifier = readModifier.get(id);
// See if this packet has been cancelled before // See if this packet has been cancelled before
@ -93,7 +92,7 @@ class PacketInjector {
private void initialize() throws IllegalAccessException { private void initialize() throws IllegalAccessException {
if (intHashMap == null) { if (intHashMap == null) {
// We're looking for the first static field with a Minecraft-object. This should be a IntHashMap. // We're looking for the first static field with a Minecraft-object. This should be a IntHashMap.
Field intHashMapField = FuzzyReflection.fromClass(Packet.class, true). Field intHashMapField = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).
getFieldByType(MinecraftReflection.MINECRAFT_OBJECT); getFieldByType(MinecraftReflection.MINECRAFT_OBJECT);
try { try {

View File

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

View File

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

View File

@ -25,7 +25,6 @@ import java.util.Set;
import com.comphenix.protocol.injector.ListenerInvoker; import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket; import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodInterceptor;
@ -36,7 +35,7 @@ import net.sf.cglib.proxy.MethodProxy;
* *
* @author Kristian * @author Kristian
*/ */
class InjectedArrayList extends ArrayList<Packet> { class InjectedArrayList extends ArrayList<Object> {
/** /**
* Silly Eclipse. * Silly Eclipse.
@ -44,12 +43,12 @@ class InjectedArrayList extends ArrayList<Packet> {
private static final long serialVersionUID = -1173865905404280990L; private static final long serialVersionUID = -1173865905404280990L;
private transient PlayerInjector injector; private transient PlayerInjector injector;
private transient Set<Packet> ignoredPackets; private transient Set<Object> ignoredPackets;
private transient ClassLoader classLoader; private transient ClassLoader classLoader;
private transient InvertedIntegerCallback callback; private transient InvertedIntegerCallback callback;
public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set<Packet> ignoredPackets) { public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set<Object> ignoredPackets) {
this.classLoader = classLoader; this.classLoader = classLoader;
this.injector = injector; this.injector = injector;
this.ignoredPackets = ignoredPackets; this.ignoredPackets = ignoredPackets;
@ -57,9 +56,9 @@ class InjectedArrayList extends ArrayList<Packet> {
} }
@Override @Override
public boolean add(Packet packet) { public boolean add(Object packet) {
Packet result = null; Object result = null;
// Check for fake packets and ignored packets // Check for fake packets and ignored packets
if (packet instanceof FakePacket) { if (packet instanceof FakePacket) {
@ -94,7 +93,7 @@ class InjectedArrayList extends ArrayList<Packet> {
* @param source - packet to invert. * @param source - packet to invert.
* @return The inverted packet. * @return The inverted packet.
*/ */
Packet createNegativePacket(Packet source) { Object createNegativePacket(Object source) {
ListenerInvoker invoker = injector.getInvoker(); ListenerInvoker invoker = injector.getInvoker();
int packetID = invoker.getPacketID(source); int packetID = invoker.getPacketID(source);
@ -133,7 +132,7 @@ class InjectedArrayList extends ArrayList<Packet> {
try { try {
// Temporarily associate the fake packet class // Temporarily associate the fake packet class
invoker.registerPacketClass(proxyClass, packetID); invoker.registerPacketClass(proxyClass, packetID);
return (Packet) proxyClass.newInstance(); return proxyClass.newInstance();
} catch (Exception e) { } catch (Exception e) {
// Don't pollute the throws tree // Don't pollute the throws tree

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,14 +27,15 @@ import java.net.SocketAddress;
import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.Factory;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitWorker;
import com.comphenix.protocol.Packets; import com.comphenix.protocol.Packets;
import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener; import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.injector.GamePhase; import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker; import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks; import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
@ -121,8 +122,8 @@ abstract class PlayerInjector {
* @return Notch player object. * @return Notch player object.
*/ */
protected Object getEntityPlayer(Player player) { protected Object getEntityPlayer(Player player) {
CraftPlayer craft = (CraftPlayer) player; BukkitUnwrapper unwrapper = new BukkitUnwrapper();
return craft.getHandle(); return unwrapper.unwrapItem(player);
} }
/** /**
@ -136,7 +137,7 @@ abstract class PlayerInjector {
//Dispatch to the correct injection method //Dispatch to the correct injection method
if (injectionSource instanceof Player) if (injectionSource instanceof Player)
initializePlayer(injectionSource); initializePlayer(injectionSource);
else if (injectionSource instanceof NetLoginHandler) else if (MinecraftReflection.isLoginHandler(injectionSource))
initializeLogin(injectionSource); initializeLogin(injectionSource);
else else
throw new IllegalArgumentException("Cannot initialize a player hook using a " + injectionSource.getClass().getName()); throw new IllegalArgumentException("Cannot initialize a player hook using a " + injectionSource.getClass().getName());
@ -148,7 +149,7 @@ abstract class PlayerInjector {
*/ */
public void initializePlayer(Object player) { public void initializePlayer(Object player) {
EntityPlayer notchEntity = getEntityPlayer((Player) player); Object notchEntity = getEntityPlayer((Player) player);
if (!hasInitialized) { if (!hasInitialized) {
// Do this first, in case we encounter an exception // Do this first, in case we encounter an exception
@ -202,7 +203,7 @@ abstract class PlayerInjector {
// And the queue method // And the queue method
if (queueMethod == null) if (queueMethod == null)
queueMethod = FuzzyReflection.fromClass(reference.getType()). queueMethod = FuzzyReflection.fromClass(reference.getType()).
getMethodByParameters("queue", Packet.class ); getMethodByParameters("queue", MinecraftReflection.getPacketClass());
// And the data input stream that we'll use to identify a player // And the data input stream that we'll use to identify a player
if (inputField == null) if (inputField == null)
@ -321,7 +322,7 @@ abstract class PlayerInjector {
} }
} }
private Field getProxyField(EntityPlayer notchEntity, Field serverField) { private Field getProxyField(Object notchEntity, Field serverField) {
try { try {
Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true); Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true);
@ -396,10 +397,10 @@ abstract class PlayerInjector {
* @return The stored entity player. * @return The stored entity player.
* @throws IllegalAccessException If the reflection failed. * @throws IllegalAccessException If the reflection failed.
*/ */
private EntityPlayer getEntityPlayer(Object netHandler) throws IllegalAccessException { private Object getEntityPlayer(Object netHandler) throws IllegalAccessException {
if (entityPlayerField == null) if (entityPlayerField == null)
entityPlayerField = FuzzyReflection.fromObject(netHandler).getFieldByType(".*EntityPlayer"); entityPlayerField = FuzzyReflection.fromObject(netHandler).getFieldByType(".*EntityPlayer");
return (EntityPlayer) FieldUtils.readField(entityPlayerField, netHandler); return FieldUtils.readField(entityPlayerField, netHandler);
} }
/** /**
@ -408,15 +409,15 @@ abstract class PlayerInjector {
* @throws IllegalAccessException If the reflection machinery failed. * @throws IllegalAccessException If the reflection machinery failed.
* @throws InvocationTargetException If the underlying method caused an error. * @throws InvocationTargetException If the underlying method caused an error.
*/ */
public void processPacket(Packet packet) throws IllegalAccessException, InvocationTargetException { public void processPacket(Object packet) throws IllegalAccessException, InvocationTargetException {
Object netHandler = getNetHandler(); Object netHandler = getNetHandler();
// Get the process method // Get the process method
if (processMethod == null) { if (processMethod == null) {
try { try {
processMethod = FuzzyReflection.fromClass(Packet.class). processMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
getMethodByParameters("processPacket", netHandlerField.getType()); getMethodByParameters("processPacket", netHandlerField.getType());
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw new IllegalArgumentException("Cannot locate process packet method: " + e.getMessage()); throw new IllegalArgumentException("Cannot locate process packet method: " + e.getMessage());
} }
@ -438,7 +439,7 @@ abstract class PlayerInjector {
* @param filtered - whether or not the packet will be filtered by our listeners. * @param filtered - whether or not the packet will be filtered by our listeners.
* @param InvocationTargetException If an error occured when sending the packet. * @param InvocationTargetException If an error occured when sending the packet.
*/ */
public abstract void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException; public abstract void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException;
/** /**
* Inject a hook to catch packets sent to the current player. * Inject a hook to catch packets sent to the current player.
@ -499,7 +500,7 @@ abstract class PlayerInjector {
* @param packet - packet to sent. * @param packet - packet to sent.
* @return The given packet, or the packet replaced by the listeners. * @return The given packet, or the packet replaced by the listeners.
*/ */
public Packet handlePacketSending(Packet packet) { public Object handlePacketSending(Object packet) {
try { try {
// Get the packet ID too // Get the packet ID too
Integer id = invoker.getPacketID(packet); Integer id = invoker.getPacketID(packet);
@ -514,7 +515,7 @@ abstract class PlayerInjector {
if (updateOnLogin) { if (updateOnLogin) {
if (id == Packets.Server.LOGIN) { if (id == Packets.Server.LOGIN) {
try { try {
updatedPlayer = getEntityPlayer(getNetHandler()).getBukkitEntity(); updatedPlayer = (Player) MinecraftReflection.getBukkitEntity(getEntityPlayer(getNetHandler()));
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
reporter.reportDetailed(this, "Cannot update player in PlayerEvent.", e, packet); reporter.reportDetailed(this, "Cannot update player in PlayerEvent.", e, packet);
} }

View File

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

@ -1,13 +1,453 @@
package com.comphenix.protocol.utility; 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. * Methods and constants specifically used in conjuction with reflecting Minecraft object.
* @author Kristian
* *
* @author Kristian
*/ */
public class MinecraftReflection { public class MinecraftReflection {
/** /**
* Matches a Minecraft object. * Regular expression that matches a Minecraft object.
*/ */
public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+"; 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_SERVER_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("Cannot find Bukkit. Is it running?");
}
}
/**
* 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_SERVER_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_SERVER_PACKAGE) && javaName.endsWith(className);
}
/**
* Dynamically retrieve the Bukkit entity from a given entity.
* @param nmsObject - the NMS entity.
* @return A bukkit entity.
* @throws RuntimeException If we were unable to retrieve the Bukkit entity.
*/
public static Object getBukkitEntity(Object nmsObject) {
if (nmsObject == null)
return null;
// We will have to do this dynamically, unfortunately
try {
return nmsObject.getClass().getMethod("getBukkitEntity").invoke(nmsObject);
} catch (Exception e) {
throw new RuntimeException("Cannot get Bukkit entity from " + nmsObject, e);
}
}
/**
* Determine if a given object is a ChunkPosition.
* @param obj - the object to test.
* @return TRUE if it can, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isChunkPosition(Object obj) {
return getChunkPositionClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if a given object is a ChunkCoordinate.
* @param obj - the object to test.
* @return TRUE if it can, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isChunkCoordinates(Object obj) {
return getChunkCoordinatesClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is actually a Minecraft packet.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isPacketClass(Object obj) {
return getPacketClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is a NetLoginHandler.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isLoginHandler(Object obj) {
return getNetLoginHandlerClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is actually a Minecraft packet.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isMinecraftEntity(Object obj) {
return getEntityClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is a NMS ItemStack.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isItemStack(Object value) {
return getItemStackClass().isAssignableFrom(value.getClass());
}
/**
* Determine if the given object is a Minecraft player entity.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isMinecraftPlayer(Object obj) {
return getEntityPlayerClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is a watchable object.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isWatchableObject(Object obj) {
return getWatchableObjectClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is a data watcher object.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isDataWatcher(Object obj) {
return getDataWatcherClass().isAssignableFrom(obj.getClass());
}
/**
* Determine if the given object is a CraftItemStack instancey.
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isCraftItemStack(Object obj) {
return getCraftItemStackClass().isAssignableFrom(obj.getClass());
}
/**
* Retrieve the EntityPlayer (NMS) class.
* @return The entity class.
*/
@SuppressWarnings("rawtypes")
public static Class getEntityPlayerClass() {
return getMinecraftClass("EntityPlayer");
}
/**
* Retrieve the entity (NMS) class.
* @return The entity class.
*/
@SuppressWarnings("rawtypes")
public static Class getEntityClass() {
return getMinecraftClass("Entity");
}
/**
* Retrieve the packet class.
* @return The packet class.
*/
@SuppressWarnings("rawtypes")
public static Class getPacketClass() {
return getMinecraftClass("Packet");
}
/**
* Retrieve the NetLoginHandler class.
* @return The NetLoginHandler class.
*/
@SuppressWarnings("rawtypes")
public static Class getNetLoginHandlerClass() {
return getMinecraftClass("NetLoginHandler");
}
/**
* Retrieve the NetLoginHandler class.
* @return The NetLoginHandler class.
*/
@SuppressWarnings("rawtypes")
public static Class getItemStackClass() {
return getMinecraftClass("ItemStack");
}
/**
* Retrieve the WorldType class.
* @return The WorldType class.
*/
@SuppressWarnings("rawtypes")
public static Class getWorldTypeClass() {
return getMinecraftClass("WorldType");
}
/**
* Retrieve the DataWatcher class.
* @return The DataWatcher class.
*/
@SuppressWarnings("rawtypes")
public static Class getDataWatcherClass() {
return getMinecraftClass("DataWatcher");
}
/**
* Retrieve the ChunkPosition class.
* @return The ChunkPosition class.
*/
@SuppressWarnings("rawtypes")
public static Class getChunkPositionClass() {
return getMinecraftClass("ChunkPosition");
}
/**
* Retrieve the ChunkPosition class.
* @return The ChunkPosition class.
*/
@SuppressWarnings("rawtypes")
public static Class getChunkCoordinatesClass() {
return getMinecraftClass("ChunkCoordinates");
}
/**
* Retrieve the WatchableObject class.
* @return The WatchableObject class.
*/
@SuppressWarnings("rawtypes")
public static Class getWatchableObjectClass() {
return getMinecraftClass("WatchableObject");
}
/**
* Retrieve the ItemStack[] class.
* @return The ItemStack[] class.
*/
@SuppressWarnings("rawtypes")
public static Class getItemStackArrayClass() {
if (itemStackArrayClass == null)
itemStackArrayClass = getArrayClass(getItemStackClass());
return itemStackArrayClass;
}
/**
* Retrieve the array class of a given component type.
* @param componentType - type of each element in the array.
* @return The class of the array.
*/
@SuppressWarnings("rawtypes")
public static Class getArrayClass(Class componentType) {
// Bit of a hack, but it works
return Array.newInstance(componentType, 0).getClass();
}
/**
* Retrieve the CraftItemStack class.
* @return The CraftItemStack class.
*/
@SuppressWarnings("rawtypes")
public static Class getCraftItemStackClass() {
if (craftItemStackClass == null)
craftItemStackClass = getCraftBukkitClass("inventory.CraftItemStack");
return craftItemStackClass;
}
/**
* Retrieve a CraftItemStack from a given ItemStack.
* @param bukkitItemStack - the Bukkit ItemStack to convert.
* @return A CraftItemStack as an ItemStack.
*/
@SuppressWarnings("unchecked")
public static ItemStack getBukkitItemStack(ItemStack bukkitItemStack) {
if (craftBukkitConstructor == null) {
try {
craftBukkitConstructor = getCraftItemStackClass().getConstructor(ItemStack.class);
} catch (Exception e) {
throw new RuntimeException("Cannot find CraftItemStack(org.bukkit.inventory.ItemStack).", e);
}
}
// Try to create the CraftItemStack
try {
return (ItemStack) craftBukkitConstructor.newInstance(bukkitItemStack);
} catch (Exception e) {
throw new RuntimeException("Cannot construct CraftItemStack.", e);
}
}
/**
* Retrieve the Bukkit ItemStack from a given net.minecraft.server ItemStack.
* @param minecraftItemStack - the NMS ItemStack to wrap.
* @return The wrapped ItemStack.
*/
@SuppressWarnings("unchecked")
public static ItemStack getBukkitItemStack(Object minecraftItemStack) {
if (craftNMSConstructor == null) {
try {
craftNMSConstructor = getCraftItemStackClass().getConstructor(minecraftItemStack.getClass());
} catch (Exception e) {
throw new RuntimeException("Cannot find CraftItemStack(net.mineraft.server.ItemStack).", e);
}
}
// Try to create the CraftItemStack
try {
return (ItemStack) craftNMSConstructor.newInstance(minecraftItemStack);
} catch (Exception e) {
throw new RuntimeException("Cannot construct CraftItemStack.", e);
}
}
/**
* Retrieve the net.minecraft.server ItemStack from a Bukkit ItemStack.
* @param stack - the Bukkit ItemStack to convert.
* @return The NMS ItemStack.
*/
public static Object getMinecraftItemStack(ItemStack stack) {
// Make sure this is a CraftItemStack
if (!isCraftItemStack(stack))
stack = getBukkitItemStack(stack);
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
return unwrapper.unwrapItem(stack);
}
/**
* Retrieve the class object of a specific CraftBukkit class.
* @param className - the specific CraftBukkit class.
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
@SuppressWarnings("rawtypes")
public static Class getCraftBukkitClass(String className) {
if (craftbukkitPackage == null)
craftbukkitPackage = new CachedPackage(getCraftBukkitPackage());
return craftbukkitPackage.getPackageClass(className);
}
/**
* Retrieve the class object of a specific Minecraft class.
* @param className - the specific Minecraft class.
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
@SuppressWarnings("rawtypes")
public static Class getMinecraftClass(String className) {
if (minecraftPackage == null)
minecraftPackage = new CachedPackage(getMinecraftPackage());
return minecraftPackage.getPackageClass(className);
}
} }

View File

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

View File

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

View File

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

View File

@ -20,11 +20,9 @@ import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import net.minecraft.server.DataWatcher;
import net.minecraft.server.WatchableObject;
/** /**
* Wraps a DataWatcher that is used to transmit arbitrary key-value pairs with a given entity. * Wraps a DataWatcher that is used to transmit arbitrary key-value pairs with a given entity.
* *
@ -55,7 +53,7 @@ public class WrappedDataWatcher {
private static boolean hasInitialized; private static boolean hasInitialized;
// The underlying DataWatcher we're modifying // The underlying DataWatcher we're modifying
protected DataWatcher handle; protected Object handle;
// Lock // Lock
private ReadWriteLock readWriteLock; private ReadWriteLock readWriteLock;
@ -69,7 +67,13 @@ public class WrappedDataWatcher {
*/ */
public WrappedDataWatcher() { public WrappedDataWatcher() {
// Just create a new watcher // Just create a new watcher
this(new DataWatcher()); try {
this.handle = MinecraftReflection.getDataWatcherClass().newInstance();
initialize();
} catch (Exception e) {
throw new RuntimeException("Unable to construct DataWatcher.", e);
}
} }
/** /**
@ -77,14 +81,14 @@ public class WrappedDataWatcher {
* @param handle - the data watcher to wrap. * @param handle - the data watcher to wrap.
* @throws FieldAccessException If we're unable to wrap a DataWatcher. * @throws FieldAccessException If we're unable to wrap a DataWatcher.
*/ */
public WrappedDataWatcher(DataWatcher handle) { public WrappedDataWatcher(Object handle) {
this.handle = handle; if (handle == null)
throw new IllegalArgumentException("Handle cannot be NULL.");
if (!MinecraftReflection.isDataWatcher(handle))
throw new IllegalArgumentException("The value " + handle + " is not a DataWatcher.");
try { this.handle = handle;
initialize(); initialize();
} catch (FieldAccessException e) {
throw new RuntimeException("Cannot initialize wrapper.", e);
}
} }
/** /**
@ -105,7 +109,7 @@ public class WrappedDataWatcher {
* Retrieves the underlying data watcher. * Retrieves the underlying data watcher.
* @return The underlying data watcher. * @return The underlying data watcher.
*/ */
public DataWatcher getHandle() { public Object getHandle() {
return handle; return handle;
} }
@ -215,7 +219,7 @@ public class WrappedDataWatcher {
*/ */
public Object getObject(int index) throws FieldAccessException { public Object getObject(int index) throws FieldAccessException {
// The get method will take care of concurrency // The get method will take care of concurrency
WatchableObject watchable = getWatchedObject(index); Object watchable = getWatchedObject(index);
if (watchable != null) { if (watchable != null) {
return new WrappedWatchableObject(watchable).getValue(); return new WrappedWatchableObject(watchable).getValue();
@ -238,7 +242,7 @@ public class WrappedDataWatcher {
// Add each watchable object to the list // Add each watchable object to the list
for (Object watchable : getWatchableObjectMap().values()) { for (Object watchable : getWatchableObjectMap().values()) {
if (watchable != null) { if (watchable != null) {
result.add(new WrappedWatchableObject((WatchableObject) watchable)); result.add(new WrappedWatchableObject(watchable));
} else { } else {
result.add(null); result.add(null);
} }
@ -305,7 +309,7 @@ public class WrappedDataWatcher {
writeLock.lock(); writeLock.lock();
try { try {
WatchableObject watchable = getWatchedObject(index); Object watchable = getWatchedObject(index);
if (watchable != null) { if (watchable != null) {
new WrappedWatchableObject(watchable).setValue(newValue, update); new WrappedWatchableObject(watchable).setValue(newValue, update);
@ -325,18 +329,18 @@ public class WrappedDataWatcher {
} }
} }
private WatchableObject getWatchedObject(int index) throws FieldAccessException { private Object getWatchedObject(int index) throws FieldAccessException {
// We use the get-method first and foremost // We use the get-method first and foremost
if (getKeyValueMethod != null) { if (getKeyValueMethod != null) {
try { try {
return (WatchableObject) getKeyValueMethod.invoke(handle, index); return getKeyValueMethod.invoke(handle, index);
} catch (Exception e) { } catch (Exception e) {
throw new FieldAccessException("Cannot invoke get key method for index " + index, e); throw new FieldAccessException("Cannot invoke get key method for index " + index, e);
} }
} else { } else {
try { try {
getReadWriteLock().readLock().lock(); getReadWriteLock().readLock().lock();
return (WatchableObject) getWatchableObjectMap().get(index); return getWatchableObjectMap().get(index);
} finally { } finally {
getReadWriteLock().readLock().unlock(); getReadWriteLock().readLock().unlock();
@ -388,8 +392,8 @@ public class WrappedDataWatcher {
*/ */
public static WrappedDataWatcher getEntityWatcher(Entity entity) throws FieldAccessException { public static WrappedDataWatcher getEntityWatcher(Entity entity) throws FieldAccessException {
if (entityDataField == null) if (entityDataField == null)
entityDataField = FuzzyReflection.fromClass(net.minecraft.server.Entity.class, true). entityDataField = FuzzyReflection.fromClass(MinecraftReflection.getEntityClass(), true).
getFieldByType("datawatcher", DataWatcher.class); getFieldByType("datawatcher", MinecraftReflection.getDataWatcherClass());
BukkitUnwrapper unwrapper = new BukkitUnwrapper(); BukkitUnwrapper unwrapper = new BukkitUnwrapper();
@ -397,7 +401,7 @@ public class WrappedDataWatcher {
Object nsmWatcher = FieldUtils.readField(entityDataField, unwrapper.unwrapItem(entity), true); Object nsmWatcher = FieldUtils.readField(entityDataField, unwrapper.unwrapItem(entity), true);
if (nsmWatcher != null) if (nsmWatcher != null)
return new WrappedDataWatcher((DataWatcher) nsmWatcher); return new WrappedDataWatcher(nsmWatcher);
else else
return null; return null;
@ -417,7 +421,7 @@ public class WrappedDataWatcher {
else else
return; return;
FuzzyReflection fuzzy = FuzzyReflection.fromClass(DataWatcher.class, true); FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getDataWatcherClass(), true);
for (Field lookup : fuzzy.getFieldListByType(Map.class)) { for (Field lookup : fuzzy.getFieldListByType(Map.class)) {
if (Modifier.isStatic(lookup.getModifiers())) { if (Modifier.isStatic(lookup.getModifiers())) {

View File

@ -1,13 +1,14 @@
package com.comphenix.protocol.wrappers; package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
import net.minecraft.server.ChunkCoordinates;
import net.minecraft.server.ItemStack;
import net.minecraft.server.WatchableObject;
/** /**
* Represents a watchable object. * Represents a watchable object.
@ -22,7 +23,10 @@ public class WrappedWatchableObject {
// The field containing the value itself // The field containing the value itself
private static StructureModifier<Object> baseModifier; private static StructureModifier<Object> baseModifier;
protected WatchableObject handle; // Used to create new watchable objects
private static Constructor<?> watchableConstructor;
protected Object handle;
protected StructureModifier<Object> modifier; protected StructureModifier<Object> modifier;
// Type of the stored value // Type of the stored value
@ -32,7 +36,7 @@ public class WrappedWatchableObject {
* Wrap a given raw Minecraft watchable object. * Wrap a given raw Minecraft watchable object.
* @param handle - the raw watchable object to wrap. * @param handle - the raw watchable object to wrap.
*/ */
public WrappedWatchableObject(WatchableObject handle) { public WrappedWatchableObject(Object handle) {
load(handle); load(handle);
} }
@ -41,6 +45,7 @@ public class WrappedWatchableObject {
* @param index - the index. * @param index - the index.
* @param value - non-null value of specific types. * @param value - non-null value of specific types.
*/ */
@SuppressWarnings("unchecked")
public WrappedWatchableObject(int index, Object value) { public WrappedWatchableObject(int index, Object value) {
if (value == null) if (value == null)
throw new IllegalArgumentException("Value cannot be NULL."); throw new IllegalArgumentException("Value cannot be NULL.");
@ -49,14 +54,28 @@ public class WrappedWatchableObject {
Integer typeID = WrappedDataWatcher.getTypeID(value.getClass()); Integer typeID = WrappedDataWatcher.getTypeID(value.getClass());
if (typeID != null) { if (typeID != null) {
load(new WatchableObject(typeID, index, getUnwrapped(value))); if (watchableConstructor == null) {
try {
watchableConstructor = MinecraftReflection.getWatchableObjectClass().
getConstructor(int.class, int.class, Object.class);
} catch (Exception e) {
throw new RuntimeException("Cannot get the WatchableObject(int, int, Object) constructor.", e);
}
}
// Create the object
try {
load(watchableConstructor.newInstance(typeID, index, getUnwrapped(value)));
} catch (Exception e) {
throw new RuntimeException("Cannot construct underlying WatchableObject.", e);
}
} else { } else {
throw new IllegalArgumentException("Cannot watch the type " + value.getClass()); throw new IllegalArgumentException("Cannot watch the type " + value.getClass());
} }
} }
// Wrap a NMS object // Wrap a NMS object
private void load(WatchableObject handle) { private void load(Object handle) {
initialize(); initialize();
this.handle = handle; this.handle = handle;
this.modifier = baseModifier.withTarget(handle); this.modifier = baseModifier.withTarget(handle);
@ -66,7 +85,7 @@ public class WrappedWatchableObject {
* Retrieves the underlying watchable object. * Retrieves the underlying watchable object.
* @return The underlying watchable object. * @return The underlying watchable object.
*/ */
public WatchableObject getHandle() { public Object getHandle() {
return handle; return handle;
} }
@ -76,7 +95,7 @@ public class WrappedWatchableObject {
private static void initialize() { private static void initialize() {
if (!hasInitialized) { if (!hasInitialized) {
hasInitialized = true; hasInitialized = true;
baseModifier = new StructureModifier<Object>(WatchableObject.class, null, false); baseModifier = new StructureModifier<Object>(MinecraftReflection.getWatchableObjectClass(), null, false);
} }
} }
@ -204,12 +223,13 @@ public class WrappedWatchableObject {
* @param value - the raw NMS object to wrap. * @param value - the raw NMS object to wrap.
* @return The wrapped object. * @return The wrapped object.
*/ */
@SuppressWarnings("rawtypes")
static Object getWrapped(Object value) { static Object getWrapped(Object value) {
// Handle the special cases // Handle the special cases
if (value instanceof net.minecraft.server.ItemStack) { if (MinecraftReflection.isItemStack(value)) {
return BukkitConverters.getItemStackConverter().getSpecific(value); return BukkitConverters.getItemStackConverter().getSpecific(value);
} else if (value instanceof ChunkCoordinates) { } else if (MinecraftReflection.isChunkCoordinates(value)) {
return new WrappedChunkCoordinate((ChunkCoordinates) value); return new WrappedChunkCoordinate((Comparable) value);
} else { } else {
return value; return value;
} }
@ -221,9 +241,9 @@ public class WrappedWatchableObject {
* @return The wrapped class type. * @return The wrapped class type.
*/ */
static Class<?> getWrappedType(Class<?> unwrapped) { static Class<?> getWrappedType(Class<?> unwrapped) {
if (unwrapped.equals(net.minecraft.server.ChunkPosition.class)) if (unwrapped.equals(MinecraftReflection.getChunkPositionClass()))
return ChunkPosition.class; return ChunkPosition.class;
else if (unwrapped.equals(ChunkCoordinates.class)) else if (unwrapped.equals(MinecraftReflection.getChunkCoordinatesClass()))
return WrappedChunkCoordinate.class; return WrappedChunkCoordinate.class;
else else
return unwrapped; return unwrapped;
@ -240,7 +260,7 @@ public class WrappedWatchableObject {
return ((WrappedChunkCoordinate) wrapped).getHandle(); return ((WrappedChunkCoordinate) wrapped).getHandle();
else if (wrapped instanceof ItemStack) else if (wrapped instanceof ItemStack)
return BukkitConverters.getItemStackConverter().getGeneric( return BukkitConverters.getItemStackConverter().getGeneric(
net.minecraft.server.ItemStack.class, (org.bukkit.inventory.ItemStack) wrapped); MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
else else
return wrapped; return wrapped;
} }
@ -252,9 +272,9 @@ public class WrappedWatchableObject {
*/ */
static Class<?> getUnwrappedType(Class<?> wrapped) { static Class<?> getUnwrappedType(Class<?> wrapped) {
if (wrapped.equals(ChunkPosition.class)) if (wrapped.equals(ChunkPosition.class))
return net.minecraft.server.ChunkPosition.class; return MinecraftReflection.getChunkPositionClass();
else if (wrapped.equals(WrappedChunkCoordinate.class)) else if (wrapped.equals(WrappedChunkCoordinate.class))
return ChunkCoordinates.class; return MinecraftReflection.getChunkCoordinatesClass();
else else
return wrapped; return wrapped;
} }
@ -265,7 +285,8 @@ public class WrappedWatchableObject {
* @throws FieldAccessException If we're unable to use reflection. * @throws FieldAccessException If we're unable to use reflection.
*/ */
public WrappedWatchableObject deepClone() throws FieldAccessException { public WrappedWatchableObject deepClone() throws FieldAccessException {
WrappedWatchableObject clone = new WrappedWatchableObject(DefaultInstances.DEFAULT.getDefault(WatchableObject.class)); @SuppressWarnings("unchecked")
WrappedWatchableObject clone = new WrappedWatchableObject(DefaultInstances.DEFAULT.getDefault(MinecraftReflection.getWatchableObjectClass()));
clone.setDirtyState(getDirtyState()); clone.setDirtyState(getDirtyState());
clone.setIndex(getIndex()); clone.setIndex(getIndex());
@ -279,11 +300,11 @@ public class WrappedWatchableObject {
Object value = getValue(); Object value = getValue();
// Only a limited set of references types are supported // Only a limited set of references types are supported
if (value instanceof net.minecraft.server.ChunkPosition) { if (MinecraftReflection.isChunkPosition(value)) {
EquivalentConverter<ChunkPosition> converter = ChunkPosition.getConverter(); EquivalentConverter<ChunkPosition> converter = ChunkPosition.getConverter();
return converter.getGeneric(net.minecraft.server.ChunkPosition.class, converter.getSpecific(value)); return converter.getGeneric(MinecraftReflection.getChunkPositionClass(), converter.getSpecific(value));
} else if (value instanceof ItemStack) { } else if (MinecraftReflection.isItemStack(value)) {
return ((ItemStack) value).cloneItemStack(); return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(value).clone());
} else { } else {
// A string or primitive wrapper, which are all immutable. // A string or primitive wrapper, which are all immutable.
return value; return value;

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