mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-27 21:26:17 +01:00
Merge branch 'master' into gh-pages
This commit is contained in:
commit
3d166ae353
@ -4,7 +4,7 @@
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<name>ProtocolLib</name>
|
||||
<version>1.7.1</version>
|
||||
<version>1.8.0</version>
|
||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||
<url>http://dev.bukkit.org/server-mods/protocollib/</url>
|
||||
<developers>
|
||||
@ -139,7 +139,7 @@
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>craftbukkit</artifactId>
|
||||
<version>1.3.2-R1.0</version>
|
||||
<version>1.4.5-R0.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>1.7.1</version>
|
||||
<version>1.8.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||
|
||||
@ -202,7 +202,7 @@
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>craftbukkit</artifactId>
|
||||
<version>1.3.2-R1.0</version>
|
||||
<version>1.4.5-R0.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -10,7 +10,6 @@ import java.util.WeakHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
@ -29,6 +28,7 @@ import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.PrettyPrinter;
|
||||
import com.comphenix.protocol.utility.ChatExtensions;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.DiscreteDomains;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.Ranges;
|
||||
@ -394,7 +394,7 @@ class CommandPacket extends CommandBase {
|
||||
// Detailed will print the packet's content too
|
||||
if (detailed) {
|
||||
try {
|
||||
Packet packet = event.getPacket().getHandle();
|
||||
Object packet = event.getPacket().getHandle();
|
||||
Class<?> clazz = packet.getClass();
|
||||
|
||||
// Get the first Minecraft super class
|
||||
@ -404,7 +404,7 @@ class CommandPacket extends CommandBase {
|
||||
}
|
||||
|
||||
logger.info(shortDescription + ":\n" +
|
||||
PrettyPrinter.printObject(packet, clazz, Packet.class)
|
||||
PrettyPrinter.printObject(packet, clazz, MinecraftReflection.getPacketClass())
|
||||
);
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
|
@ -47,6 +47,7 @@ class CommandProtocol extends CommandBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void checkVersion(final CommandSender sender) {
|
||||
// Perform on an async thread
|
||||
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() {
|
||||
@ -64,6 +65,7 @@ class CommandProtocol extends CommandBase {
|
||||
updateFinished();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void updateVersion(final CommandSender sender) {
|
||||
// Perform on an async thread
|
||||
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() {
|
||||
|
@ -0,0 +1,161 @@
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bukkit.Server;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
|
||||
/**
|
||||
* Determine the current Minecraft version.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class MinecraftVersion implements Comparable<MinecraftVersion> {
|
||||
/**
|
||||
* Regular expression used to parse version strings.
|
||||
*/
|
||||
private static final String VERSION_PATTERN = ".*\\(MC:\\s*((?:\\d+\\.)*\\d)\\s*\\)";
|
||||
|
||||
private final int major;
|
||||
private final int minor;
|
||||
private final int build;
|
||||
|
||||
/**
|
||||
* Determine the current Minecraft version.
|
||||
* @param server - the Bukkit server that will be used to examine the MC version.
|
||||
*/
|
||||
public MinecraftVersion(Server server) {
|
||||
this(extractVersion(server.getVersion()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a version object from the format major.minor.build.
|
||||
* @param versionOnly - the version in text form.
|
||||
*/
|
||||
public MinecraftVersion(String versionOnly) {
|
||||
int[] numbers = parseVersion(versionOnly);
|
||||
|
||||
this.major = numbers[0];
|
||||
this.minor = numbers[1];
|
||||
this.build = numbers[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a version object directly.
|
||||
* @param major - major version number.
|
||||
* @param minor - minor version number.
|
||||
* @param build - build version number.
|
||||
*/
|
||||
public MinecraftVersion(int major, int minor, int build) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.build = build;
|
||||
}
|
||||
|
||||
private int[] parseVersion(String version) {
|
||||
String[] elements = version.split("\\.");
|
||||
int[] numbers = new int[3];
|
||||
|
||||
// Make sure it's even a valid version
|
||||
if (elements.length < 1)
|
||||
throw new IllegalStateException("Corrupt MC version: " + version);
|
||||
|
||||
// The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively.
|
||||
for (int i = 0; i < Math.min(numbers.length, elements.length); i++)
|
||||
numbers[i] = Integer.parseInt(elements[i].trim());
|
||||
return numbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Major version number
|
||||
* @return Current major version number.
|
||||
*/
|
||||
public int getMajor() {
|
||||
return major;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minor version number
|
||||
* @return Current minor version number.
|
||||
*/
|
||||
public int getMinor() {
|
||||
return minor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build version number
|
||||
* @return Current build version number.
|
||||
*/
|
||||
public int getBuild() {
|
||||
return build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the version String (major.minor.build) only.
|
||||
* @return A normal version string.
|
||||
*/
|
||||
public String getVersion() {
|
||||
return String.format("%s.%s.%s", major, minor, build);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(MinecraftVersion o) {
|
||||
if (o == null)
|
||||
return 1;
|
||||
|
||||
return ComparisonChain.start().
|
||||
compare(major, o.major).
|
||||
compare(minor, o.minor).
|
||||
compare(build, o.build).
|
||||
result();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (obj == this)
|
||||
return true;
|
||||
|
||||
if (obj instanceof MinecraftVersion) {
|
||||
MinecraftVersion other = (MinecraftVersion) obj;
|
||||
|
||||
return major == other.major &&
|
||||
minor == other.minor &&
|
||||
build == other.build;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(major, minor, build);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// Convert to a String that we can parse back again
|
||||
return String.format("(MC: %s)", getVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the Minecraft version from CraftBukkit itself.
|
||||
* @param server - the server object representing CraftBukkit.
|
||||
* @return The underlying MC version.
|
||||
* @throws IllegalStateException If we could not parse the version string.
|
||||
*/
|
||||
public static String extractVersion(String text) {
|
||||
Pattern versionPattern = Pattern.compile(VERSION_PATTERN);
|
||||
Matcher version = versionPattern.matcher(text);
|
||||
|
||||
if (version.matches() && version.group(1) != null) {
|
||||
return version.group(1);
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot parse version String '" + text + "'");
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,8 @@ class ProtocolConfig {
|
||||
|
||||
private static final String METRICS_ENABLED = "metrics";
|
||||
|
||||
private static final String IGNORE_VERSION_CHECK = "ignore version check";
|
||||
|
||||
private static final String BACKGROUND_COMPILER_ENABLED = "background compiler";
|
||||
|
||||
private static final String UPDATER_NOTIFY = "notify";
|
||||
@ -148,6 +150,26 @@ class ProtocolConfig {
|
||||
public long getAutoLastTime() {
|
||||
return updater.getLong(UPDATER_LAST_TIME, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The version of Minecraft to ignore the built-in safety feature.
|
||||
* @return The version to ignore ProtocolLib's satefy.
|
||||
*/
|
||||
public String getIgnoreVersionCheck() {
|
||||
return global.getString(IGNORE_VERSION_CHECK, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets under which version of Minecraft the version safety feature will be ignored.
|
||||
* <p>
|
||||
* This is useful if a server operator has tested and verified that a version of ProtocolLib works,
|
||||
* but doesn't want or can't upgrade to a newer version.
|
||||
*
|
||||
* @param ignoreVersion - the version of Minecraft where the satefy will be disabled.
|
||||
*/
|
||||
public void setIgnoreVersionCheck(String ignoreVersion) {
|
||||
global.set(IGNORE_VERSION_CHECK, ignoreVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether or not metrics is enabled.
|
||||
|
@ -19,6 +19,7 @@ package com.comphenix.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -43,6 +44,15 @@ import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ProtocolLibrary extends JavaPlugin {
|
||||
/**
|
||||
* The minimum version ProtocolLib has been tested with.
|
||||
*/
|
||||
private static final String MINIMUM_MINECRAFT_VERSION = "1.0.0";
|
||||
|
||||
/**
|
||||
* The maximum version ProtocolLib has been tested with,
|
||||
*/
|
||||
private static final String MAXIMUM_MINECRAFT_VERSION = "1.4.5";
|
||||
|
||||
/**
|
||||
* The number of milliseconds per second.
|
||||
@ -120,7 +130,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
commandPacket = new CommandPacket(detailedReporter, this, logger, protocolManager);
|
||||
|
||||
// Send logging information to player listeners too
|
||||
broadcastUsers(PERMISSION_INFO);
|
||||
setupBroadcastUsers(PERMISSION_INFO);
|
||||
|
||||
} catch (Throwable e) {
|
||||
detailedReporter.reportDetailed(this, "Cannot load ProtocolLib.", e, protocolManager);
|
||||
@ -142,7 +152,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastUsers(final String permission) {
|
||||
private void setupBroadcastUsers(final String permission) {
|
||||
// Guard against multiple calls
|
||||
if (redirectHandler != null)
|
||||
return;
|
||||
@ -151,7 +161,10 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
redirectHandler = new Handler() {
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
commandPacket.broadcastMessageSilently(record.getMessage(), permission);
|
||||
// Only display warnings and above
|
||||
if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
|
||||
commandPacket.broadcastMessageSilently(record.getMessage(), permission);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -188,13 +201,13 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
logger.info("Structure compiler thread has been disabled.");
|
||||
}
|
||||
|
||||
// Handle unexpected Minecraft versions
|
||||
verifyMinecraftVersion();
|
||||
|
||||
// Set up command handlers
|
||||
registerCommand(CommandProtocol.NAME, commandProtocol);
|
||||
registerCommand(CommandPacket.NAME, commandPacket);
|
||||
|
||||
// Notify server managers of incompatible plugins
|
||||
checkForIncompatibility(manager);
|
||||
|
||||
// Player login and logout events
|
||||
protocolManager.registerEvents(manager, this);
|
||||
|
||||
@ -220,6 +233,26 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
// Used to check Minecraft version
|
||||
private void verifyMinecraftVersion() {
|
||||
try {
|
||||
MinecraftVersion minimum = new MinecraftVersion(MINIMUM_MINECRAFT_VERSION);
|
||||
MinecraftVersion maximum = new MinecraftVersion(MAXIMUM_MINECRAFT_VERSION);
|
||||
MinecraftVersion current = new MinecraftVersion(getServer());
|
||||
|
||||
// Skip certain versions
|
||||
if (!config.getIgnoreVersionCheck().equals(current.getVersion())) {
|
||||
// We'll just warn the user for now
|
||||
if (current.compareTo(minimum) < 0)
|
||||
logger.warning("Version " + current + " is lower than the minimum " + minimum);
|
||||
if (current.compareTo(maximum) > 0)
|
||||
logger.warning("Version " + current + " has not yet been tested! Proceed with caution.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
reporter.reportWarning(this, "Unable to retrieve current Minecraft version.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCommand(String name, CommandExecutor executor) {
|
||||
try {
|
||||
if (executor == null)
|
||||
@ -296,18 +329,6 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForIncompatibility(PluginManager manager) {
|
||||
// Plugin authors: Notify me to remove these
|
||||
String[] incompatiblePlugins = {};
|
||||
|
||||
for (String plugin : incompatiblePlugins) {
|
||||
if (manager.getPlugin(plugin) != null) {
|
||||
// Check for versions, ect.
|
||||
reporter.reportWarning(this, "Detected incompatible plugin: " + plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
// Disable compiler
|
||||
|
@ -242,7 +242,7 @@ public class AsyncListenerHandler {
|
||||
|
||||
final AsyncRunnable listenerLoop = getListenerLoop();
|
||||
|
||||
filterManager.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), new Runnable() {
|
||||
scheduleAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Thread thread = Thread.currentThread();
|
||||
@ -290,7 +290,7 @@ public class AsyncListenerHandler {
|
||||
final AsyncRunnable listenerLoop = getListenerLoop();
|
||||
final Function<AsyncRunnable, Void> delegateCopy = executor;
|
||||
|
||||
filterManager.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), new Runnable() {
|
||||
scheduleAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delegateCopy.apply(listenerLoop);
|
||||
@ -298,6 +298,11 @@ public class AsyncListenerHandler {
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void scheduleAsync(Runnable runnable) {
|
||||
filterManager.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a friendly thread name using the following convention:
|
||||
* <p><code>
|
||||
|
@ -25,13 +25,12 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
|
||||
import com.comphenix.protocol.PacketStream;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.PrioritizedListener;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
/**
|
||||
@ -403,10 +402,10 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
|
||||
if (isMinecraftAsync == null && !alwaysSync) {
|
||||
try {
|
||||
isMinecraftAsync = FuzzyReflection.fromClass(Packet.class).getMethodByName("a_.*");
|
||||
isMinecraftAsync = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethodByName("a_.*");
|
||||
} catch (RuntimeException e) {
|
||||
// This will occur in 1.2.5 (or possibly in later versions)
|
||||
List<Method> methods = FuzzyReflection.fromClass(Packet.class).
|
||||
List<Method> methods = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
|
||||
getMethodListByParameters(boolean.class, new Class[] {});
|
||||
|
||||
// Try to look for boolean methods
|
||||
|
@ -95,6 +95,21 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) {
|
||||
reportMinimal(sender, methodName, error);
|
||||
|
||||
// Print parameters, if they are given
|
||||
if (parameters != null && parameters.length > 0) {
|
||||
logger.log(Level.SEVERE, " Parameters:");
|
||||
|
||||
// Print each parameter
|
||||
for (Object parameter : parameters) {
|
||||
logger.log(Level.SEVERE, " " + getStringDescription(parameter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportMinimal(Plugin sender, String methodName, Throwable error) {
|
||||
logger.log(Level.SEVERE, "[" + PLUGIN_NAME + "] Unhandled exception occured in " + methodName + " for " +
|
||||
|
@ -12,6 +12,15 @@ public interface ErrorReporter {
|
||||
*/
|
||||
public abstract void reportMinimal(Plugin sender, String methodName, Throwable error);
|
||||
|
||||
/**
|
||||
* Prints a small minimal error report about an exception from another plugin.
|
||||
* @param sender - the other plugin.
|
||||
* @param methodName - name of the caller method.
|
||||
* @param error - the exception itself.
|
||||
* @param parameters - any relevant parameters to print.
|
||||
*/
|
||||
public abstract void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters);
|
||||
|
||||
/**
|
||||
* Prints a warning message from the current plugin.
|
||||
* @param sender - the object containing the caller method.
|
||||
|
@ -29,6 +29,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.WorldType;
|
||||
@ -39,12 +40,12 @@ import com.comphenix.protocol.injector.StructureCache;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.ChunkPosition;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Represents a Minecraft packet indirectly.
|
||||
@ -59,15 +60,15 @@ public class PacketContainer implements Serializable {
|
||||
private static final long serialVersionUID = 2074805748222377230L;
|
||||
|
||||
protected int id;
|
||||
protected transient Packet handle;
|
||||
protected transient Object handle;
|
||||
|
||||
// Current structure modifier
|
||||
protected transient StructureModifier<Object> structureModifier;
|
||||
|
||||
|
||||
// Support for serialization
|
||||
private static Method writeMethod;
|
||||
private static Method readMethod;
|
||||
|
||||
private static ConcurrentMap<Class<?>, Method> writeMethods = Maps.newConcurrentMap();
|
||||
private static ConcurrentMap<Class<?>, Method> readMethods = Maps.newConcurrentMap();
|
||||
|
||||
/**
|
||||
* Creates a packet container for a new packet.
|
||||
* @param id - ID of the packet to create.
|
||||
@ -81,7 +82,7 @@ public class PacketContainer implements Serializable {
|
||||
* @param id - ID of the given packet.
|
||||
* @param handle - contained packet.
|
||||
*/
|
||||
public PacketContainer(int id, Packet handle) {
|
||||
public PacketContainer(int id, Object handle) {
|
||||
this(id, handle, StructureCache.getStructure(id).withTarget(handle));
|
||||
}
|
||||
|
||||
@ -91,7 +92,7 @@ public class PacketContainer implements Serializable {
|
||||
* @param handle - contained packet.
|
||||
* @param structure - structure modifier.
|
||||
*/
|
||||
public PacketContainer(int id, Packet handle, StructureModifier<Object> structure) {
|
||||
public PacketContainer(int id, Object handle, StructureModifier<Object> structure) {
|
||||
if (handle == null)
|
||||
throw new IllegalArgumentException("handle cannot be null.");
|
||||
|
||||
@ -100,11 +101,17 @@ public class PacketContainer implements Serializable {
|
||||
this.structureModifier = structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* For serialization.
|
||||
*/
|
||||
protected PacketContainer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the underlying Minecraft packet.
|
||||
* @return Underlying Minecraft packet.
|
||||
*/
|
||||
public Packet getHandle() {
|
||||
public Object getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -214,7 +221,7 @@ public class PacketContainer implements Serializable {
|
||||
public StructureModifier<ItemStack> getItemModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<ItemStack>withType(
|
||||
net.minecraft.server.ItemStack.class, BukkitConverters.getItemStackConverter());
|
||||
MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,23 +237,23 @@ public class PacketContainer implements Serializable {
|
||||
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<ItemStack[]>withType(
|
||||
net.minecraft.server.ItemStack[].class,
|
||||
MinecraftReflection.getItemStackArrayClass(),
|
||||
BukkitConverters.getIgnoreNull(new EquivalentConverter<ItemStack[]>() {
|
||||
|
||||
public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
|
||||
net.minecraft.server.ItemStack[] result = new net.minecraft.server.ItemStack[specific.length];
|
||||
Object[] result = new Object[specific.length];
|
||||
|
||||
// Unwrap every item
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = (net.minecraft.server.ItemStack) stackConverter.getGeneric(
|
||||
net.minecraft.server.ItemStack.class, specific[i]);
|
||||
result[i] = stackConverter.getGeneric(
|
||||
MinecraftReflection.getItemStackClass(), specific[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getSpecific(Object generic) {
|
||||
net.minecraft.server.ItemStack[] input = (net.minecraft.server.ItemStack[]) generic;
|
||||
Object[] input = (Object[]) generic;
|
||||
ItemStack[] result = new ItemStack[input.length];
|
||||
|
||||
// Add the wrapper
|
||||
@ -273,7 +280,7 @@ public class PacketContainer implements Serializable {
|
||||
public StructureModifier<WorldType> getWorldTypeModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<WorldType>withType(
|
||||
net.minecraft.server.WorldType.class,
|
||||
MinecraftReflection.getWorldTypeClass(),
|
||||
BukkitConverters.getWorldTypeConverter());
|
||||
}
|
||||
|
||||
@ -284,7 +291,7 @@ public class PacketContainer implements Serializable {
|
||||
public StructureModifier<WrappedDataWatcher> getDataWatcherModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<WrappedDataWatcher>withType(
|
||||
net.minecraft.server.DataWatcher.class,
|
||||
MinecraftReflection.getDataWatcherClass(),
|
||||
BukkitConverters.getDataWatcherConverter());
|
||||
}
|
||||
|
||||
@ -311,7 +318,7 @@ public class PacketContainer implements Serializable {
|
||||
public StructureModifier<ChunkPosition> getPositionModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.withType(
|
||||
net.minecraft.server.ChunkPosition.class,
|
||||
MinecraftReflection.getChunkPositionClass(),
|
||||
ChunkPosition.getConverter());
|
||||
}
|
||||
|
||||
@ -327,7 +334,7 @@ public class PacketContainer implements Serializable {
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(
|
||||
net.minecraft.server.ChunkPosition.class,
|
||||
MinecraftReflection.getChunkPositionClass(),
|
||||
ChunkPosition.getConverter())
|
||||
);
|
||||
}
|
||||
@ -344,7 +351,7 @@ public class PacketContainer implements Serializable {
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(
|
||||
net.minecraft.server.WatchableObject.class,
|
||||
MinecraftReflection.getWatchableObjectClass(),
|
||||
BukkitConverters.getWatchableObjectConverter())
|
||||
);
|
||||
}
|
||||
@ -399,14 +406,12 @@ public class PacketContainer implements Serializable {
|
||||
|
||||
// We'll take care of NULL packets as well
|
||||
output.writeBoolean(handle != null);
|
||||
|
||||
// Retrieve the write method by reflection
|
||||
if (writeMethod == null)
|
||||
writeMethod = FuzzyReflection.fromObject(handle).getMethodByParameters("write", DataOutputStream.class);
|
||||
|
||||
|
||||
try {
|
||||
// Call the write-method
|
||||
writeMethod.invoke(handle, new DataOutputStream(output));
|
||||
getMethodLazily(writeMethods, handle.getClass(), "write", DataOutputStream.class).
|
||||
invoke(handle, new DataOutputStream(output));
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Minecraft packet doesn't support DataOutputStream", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
@ -429,13 +434,11 @@ public class PacketContainer implements Serializable {
|
||||
// Create a default instance of the packet
|
||||
handle = StructureCache.newPacket(id);
|
||||
|
||||
// Retrieve the read method by reflection
|
||||
if (readMethod == null)
|
||||
readMethod = FuzzyReflection.fromObject(handle).getMethodByParameters("read", DataInputStream.class);
|
||||
|
||||
// Call the read method
|
||||
try {
|
||||
readMethod.invoke(handle, new DataInputStream(input));
|
||||
getMethodLazily(readMethods, handle.getClass(), "read", DataInputStream.class).
|
||||
invoke(handle, new DataInputStream(input));
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Minecraft packet doesn't support DataInputStream", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
@ -448,4 +451,30 @@ public class PacketContainer implements Serializable {
|
||||
structureModifier = structureModifier.withTarget(handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cached method concurrently.
|
||||
* @param lookup - a lazy lookup cache.
|
||||
* @param handleClass - class type of the current packet.
|
||||
* @param methodName - name of method to retrieve.
|
||||
* @param parameterClass - the one parameter type in the method.
|
||||
* @return Reflected method.
|
||||
*/
|
||||
private Method getMethodLazily(ConcurrentMap<Class<?>, Method> lookup,
|
||||
Class<?> handleClass, String methodName, Class<?> parameterClass) {
|
||||
Method method = lookup.get(handleClass);
|
||||
|
||||
// Atomic operation
|
||||
if (method == null) {
|
||||
Method initialized = FuzzyReflection.fromClass(handleClass).getMethodByParameters(methodName, parameterClass);
|
||||
method = lookup.putIfAbsent(handleClass, initialized);
|
||||
|
||||
// Use our version if we succeeded
|
||||
if (method == null) {
|
||||
method = initialized;
|
||||
}
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
@ -28,17 +28,14 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.server.EntityPlayer;
|
||||
import net.minecraft.server.EntityTrackerEntry;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
@ -51,10 +48,11 @@ class EntityUtilities {
|
||||
private static Field entityTrackerField;
|
||||
private static Field trackedEntitiesField;
|
||||
private static Field trackedPlayersField;
|
||||
private static Field trackerField;
|
||||
|
||||
private static Method hashGetMethod;
|
||||
private static Method scanPlayersMethod;
|
||||
|
||||
|
||||
/*
|
||||
* While this function may look pretty bad, it's essentially just a reflection-warped
|
||||
* version of the following:
|
||||
@ -142,9 +140,8 @@ class EntityUtilities {
|
||||
|
||||
// Wrap every player - we also ensure that the underlying tracker list is immutable
|
||||
for (Object tracker : trackedPlayers) {
|
||||
if (tracker instanceof EntityPlayer) {
|
||||
EntityPlayer nmsPlayer = (EntityPlayer) tracker;
|
||||
result.add(nmsPlayer.getBukkitEntity());
|
||||
if (MinecraftReflection.isMinecraftPlayer(tracker)) {
|
||||
result.add((Player) MinecraftReflection.getBukkitEntity(tracker));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -164,7 +161,8 @@ class EntityUtilities {
|
||||
* @throws FieldAccessException
|
||||
*/
|
||||
private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
|
||||
Object worldServer = ((CraftWorld) world).getHandle();
|
||||
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
|
||||
Object worldServer = unwrapper.unwrapItem(world);
|
||||
|
||||
// We have to rely on the class naming here.
|
||||
if (entityTrackerField == null)
|
||||
@ -193,7 +191,7 @@ class EntityUtilities {
|
||||
|
||||
// The Minecraft field that's NOT filled in by the constructor
|
||||
trackedEntitiesField = FuzzyReflection.fromObject(tracker, true).
|
||||
getFieldByType(FuzzyReflection.MINECRAFT_OBJECT, ignoredTypes);
|
||||
getFieldByType(MinecraftReflection.MINECRAFT_OBJECT, ignoredTypes);
|
||||
}
|
||||
|
||||
// Read the entity hashmap
|
||||
@ -247,17 +245,24 @@ class EntityUtilities {
|
||||
*/
|
||||
public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException {
|
||||
try {
|
||||
EntityTrackerEntry trackerEntry = (EntityTrackerEntry) getEntityTrackerEntry(world, entityID);
|
||||
|
||||
Object trackerEntry = getEntityTrackerEntry(world, entityID);
|
||||
Object tracker = null;
|
||||
|
||||
// Handle NULL cases
|
||||
if (trackerEntry != null && trackerEntry.tracker != null) {
|
||||
return trackerEntry.tracker.getBukkitEntity();
|
||||
} else {
|
||||
return null;
|
||||
if (trackerEntry != null) {
|
||||
if (trackerField == null)
|
||||
trackerField = trackerEntry.getClass().getField("tracker");
|
||||
tracker = FieldUtils.readField(trackerField, trackerEntry, true);
|
||||
}
|
||||
|
||||
// If the tracker is NULL, we'll just assume this entity doesn't exist
|
||||
if (tracker != null)
|
||||
return (Entity) MinecraftReflection.getBukkitEntity(tracker);
|
||||
else
|
||||
return null;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new FieldAccessException("Cannot find entity from ID.", e);
|
||||
throw new FieldAccessException("Cannot find entity from ID " + entityID + ".", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,6 @@
|
||||
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
/**
|
||||
@ -45,7 +43,7 @@ public interface ListenerInvoker {
|
||||
* @param packet - the packet.
|
||||
* @return The packet ID.
|
||||
*/
|
||||
public abstract int getPacketID(Packet packet);
|
||||
public abstract int getPacketID(Object packet);
|
||||
|
||||
/**
|
||||
* Associate a given class with the given packet ID. Internal method.
|
||||
|
@ -23,11 +23,12 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
@ -77,7 +78,7 @@ class MinecraftRegistry {
|
||||
*/
|
||||
private static FuzzyReflection getPacketRegistry() {
|
||||
if (packetRegistry == null)
|
||||
packetRegistry = FuzzyReflection.fromClass(Packet.class, true);
|
||||
packetRegistry = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true);
|
||||
return packetRegistry;
|
||||
}
|
||||
|
||||
@ -166,7 +167,7 @@ class MinecraftRegistry {
|
||||
|
||||
// Optimized lookup
|
||||
if (lookup.containsKey(packetID)) {
|
||||
return lookup.get(packetID);
|
||||
return removeEnhancer(lookup.get(packetID), forceVanilla);
|
||||
}
|
||||
|
||||
// Will most likely not be used
|
||||
@ -174,10 +175,27 @@ class MinecraftRegistry {
|
||||
if (Objects.equal(entry.getValue(), packetID)) {
|
||||
// Attempt to get the vanilla class here too
|
||||
if (!forceVanilla || entry.getKey().getName().startsWith("net.minecraft.server"))
|
||||
return entry.getKey();
|
||||
return removeEnhancer(entry.getKey(), forceVanilla);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("The packet ID " + packetID + " is not registered.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first superclass that is not a CBLib proxy object.
|
||||
* @param clazz - the class whose hierachy we're going to search through.
|
||||
* @param remove - whether or not to skip enhanced (proxy) classes.
|
||||
* @return If remove is TRUE, the first superclass that is not a proxy.
|
||||
*/
|
||||
private static Class removeEnhancer(Class clazz, boolean remove) {
|
||||
if (remove) {
|
||||
// Get the underlying vanilla class
|
||||
while (Factory.class.isAssignableFrom(clazz) && !clazz.equals(Object.class)) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,6 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@ -157,7 +155,7 @@ public class PacketConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
Packet nmsPacket = (Packet) constructorMethod.newInstance(values);
|
||||
Object nmsPacket = constructorMethod.newInstance(values);
|
||||
return new PacketContainer(packetID, nmsPacket);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
@ -55,6 +54,7 @@ import com.comphenix.protocol.events.*;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@ -508,7 +508,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
if (packetCreation.compareAndSet(false, true))
|
||||
incrementPhases(GamePhase.PLAYING);
|
||||
|
||||
Packet mcPacket = packet.getHandle();
|
||||
Object mcPacket = packet.getHandle();
|
||||
|
||||
// Make sure the packet isn't cancelled
|
||||
packetInjector.undoCancel(packet.getID(), mcPacket);
|
||||
@ -686,9 +686,11 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPacketID(Packet packet) {
|
||||
public int getPacketID(Object packet) {
|
||||
if (packet == null)
|
||||
throw new IllegalArgumentException("Packet cannot be NULL.");
|
||||
if (!MinecraftReflection.isPacketClass(packet))
|
||||
throw new IllegalArgumentException("The given object " + packet + " is not a packet.");
|
||||
|
||||
return MinecraftRegistry.getPacketToID().get(packet.getClass());
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
|
||||
@ -37,6 +36,7 @@ import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
/**
|
||||
* This class is responsible for adding or removing proxy objects that intercepts recieved packets.
|
||||
@ -80,7 +80,7 @@ class PacketInjector {
|
||||
* @param id - the id of the packet.
|
||||
* @param packet - packet to uncancel.
|
||||
*/
|
||||
public void undoCancel(Integer id, Packet packet) {
|
||||
public void undoCancel(Integer id, Object packet) {
|
||||
ReadPacketModifier modifier = readModifier.get(id);
|
||||
|
||||
// See if this packet has been cancelled before
|
||||
@ -92,7 +92,8 @@ class PacketInjector {
|
||||
private void initialize() throws IllegalAccessException {
|
||||
if (intHashMap == null) {
|
||||
// We're looking for the first static field with a Minecraft-object. This should be a IntHashMap.
|
||||
Field intHashMapField = FuzzyReflection.fromClass(Packet.class, true).getFieldByType(FuzzyReflection.MINECRAFT_OBJECT);
|
||||
Field intHashMapField = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).
|
||||
getFieldByType(MinecraftReflection.MINECRAFT_OBJECT);
|
||||
|
||||
try {
|
||||
intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true);
|
||||
|
@ -29,7 +29,6 @@ import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
@ -61,7 +60,7 @@ class ReadPacketModifier implements MethodInterceptor {
|
||||
* Remove any packet overrides.
|
||||
* @param packet - the packet to rever
|
||||
*/
|
||||
public void removeOverride(Packet packet) {
|
||||
public void removeOverride(Object packet) {
|
||||
override.remove(packet);
|
||||
}
|
||||
|
||||
@ -70,7 +69,7 @@ class ReadPacketModifier implements MethodInterceptor {
|
||||
* @param packet - the given packet.
|
||||
* @return Overriden object.
|
||||
*/
|
||||
public Object getOverride(Packet packet) {
|
||||
public Object getOverride(Object packet) {
|
||||
return override.get(packet);
|
||||
}
|
||||
|
||||
@ -79,7 +78,7 @@ class ReadPacketModifier implements MethodInterceptor {
|
||||
* @param packet - the packet to check.
|
||||
* @return TRUE if it has been cancelled, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasCancelled(Packet packet) {
|
||||
public boolean hasCancelled(Object packet) {
|
||||
return getOverride(packet) == CANCEL_MARKER;
|
||||
}
|
||||
|
||||
@ -121,12 +120,12 @@ class ReadPacketModifier implements MethodInterceptor {
|
||||
DataInputStream input = (DataInputStream) args[0];
|
||||
|
||||
// Let the people know
|
||||
PacketContainer container = new PacketContainer(packetID, (Packet) thisObj);
|
||||
PacketContainer container = new PacketContainer(packetID, thisObj);
|
||||
PacketEvent event = packetInjector.packetRecieved(container, input);
|
||||
|
||||
// Handle override
|
||||
if (event != null) {
|
||||
Packet result = event.getPacket().getHandle();
|
||||
Object result = event.getPacket().getHandle();
|
||||
|
||||
if (event.isCancelled()) {
|
||||
override.put(thisObj, CANCEL_MARKER);
|
||||
|
@ -48,7 +48,8 @@ public final class SortedPacketListenerList extends AbstractConcurrentListenerMu
|
||||
element.getListener().onPacketReceiving(event);
|
||||
} catch (Throwable e) {
|
||||
// Minecraft doesn't want your Exception.
|
||||
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketReceiving()", e);
|
||||
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketReceiving(PacketEvent)", e,
|
||||
event.getPacket().getHandle());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,7 +70,8 @@ public final class SortedPacketListenerList extends AbstractConcurrentListenerMu
|
||||
element.getListener().onPacketSending(event);
|
||||
} catch (Throwable e) {
|
||||
// Minecraft doesn't want your Exception.
|
||||
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketSending()", e);
|
||||
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketSending(PacketEvent)", e,
|
||||
event.getPacket().getHandle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,12 +22,11 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||
import com.comphenix.protocol.reflect.compiler.CompileListener;
|
||||
import com.comphenix.protocol.reflect.compiler.CompiledStructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
/**
|
||||
* Caches structure modifiers.
|
||||
@ -45,9 +44,9 @@ public class StructureCache {
|
||||
* @param id - packet ID.
|
||||
* @return Created packet.
|
||||
*/
|
||||
public static Packet newPacket(int id) {
|
||||
public static Object newPacket(int id) {
|
||||
try {
|
||||
return (Packet) MinecraftRegistry.getPacketClassFromID(id, true).newInstance();
|
||||
return MinecraftRegistry.getPacketClassFromID(id, true).newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
return null;
|
||||
} catch (IllegalAccessException e) {
|
||||
@ -79,7 +78,7 @@ public class StructureCache {
|
||||
if (result == null) {
|
||||
// Use the vanilla class definition
|
||||
final StructureModifier<Object> value = new StructureModifier<Object>(
|
||||
MinecraftRegistry.getPacketClassFromID(id, true), Packet.class, true);
|
||||
MinecraftRegistry.getPacketClassFromID(id, true), MinecraftReflection.getPacketClass(), true);
|
||||
|
||||
result = structureModifiers.putIfAbsent(id, value);
|
||||
|
||||
|
@ -25,7 +25,7 @@ import java.util.Set;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
@ -35,7 +35,7 @@ import net.sf.cglib.proxy.MethodProxy;
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class InjectedArrayList extends ArrayList<Packet> {
|
||||
class InjectedArrayList extends ArrayList<Object> {
|
||||
|
||||
/**
|
||||
* Silly Eclipse.
|
||||
@ -43,19 +43,22 @@ class InjectedArrayList extends ArrayList<Packet> {
|
||||
private static final long serialVersionUID = -1173865905404280990L;
|
||||
|
||||
private transient PlayerInjector injector;
|
||||
private transient Set<Packet> ignoredPackets;
|
||||
private transient Set<Object> ignoredPackets;
|
||||
private transient ClassLoader classLoader;
|
||||
|
||||
public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set<Packet> ignoredPackets) {
|
||||
private transient InvertedIntegerCallback callback;
|
||||
|
||||
public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set<Object> ignoredPackets) {
|
||||
this.classLoader = classLoader;
|
||||
this.injector = injector;
|
||||
this.ignoredPackets = ignoredPackets;
|
||||
this.callback = new InvertedIntegerCallback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Packet packet) {
|
||||
public boolean add(Object packet) {
|
||||
|
||||
Packet result = null;
|
||||
Object result = null;
|
||||
|
||||
// Check for fake packets and ignored packets
|
||||
if (packet instanceof FakePacket) {
|
||||
@ -90,11 +93,13 @@ class InjectedArrayList extends ArrayList<Packet> {
|
||||
* @param source - packet to invert.
|
||||
* @return The inverted packet.
|
||||
*/
|
||||
Packet createNegativePacket(Packet source) {
|
||||
Object createNegativePacket(Object source) {
|
||||
ListenerInvoker invoker = injector.getInvoker();
|
||||
|
||||
int packetID = invoker.getPacketID(source);
|
||||
Class<?> type = invoker.getPacketClassFromID(packetID, true);
|
||||
|
||||
System.out.println(type.getName());
|
||||
|
||||
// We want to subtract the byte amount that were added to the running
|
||||
// total of outstanding packets. Otherwise, cancelling too many packets
|
||||
@ -122,15 +127,20 @@ class InjectedArrayList extends ArrayList<Packet> {
|
||||
ex.setCallbackType(InvertedIntegerCallback.class);
|
||||
|
||||
Class<?> proxyClass = ex.createClass();
|
||||
|
||||
// Temporarily associate the fake packet class
|
||||
invoker.registerPacketClass(proxyClass, packetID);
|
||||
|
||||
Packet fake = (Packet) Enhancer.create(proxyClass, new InvertedIntegerCallback());
|
||||
Enhancer.registerCallbacks(proxyClass, new Callback[] { callback });
|
||||
|
||||
// Remove this association
|
||||
invoker.unregisterPacketClass(proxyClass);
|
||||
return fake;
|
||||
try {
|
||||
// Temporarily associate the fake packet class
|
||||
invoker.registerPacketClass(proxyClass, packetID);
|
||||
return proxyClass.newInstance();
|
||||
|
||||
} catch (Exception e) {
|
||||
// Don't pollute the throws tree
|
||||
throw new RuntimeException("Cannot create fake class.", e);
|
||||
} finally {
|
||||
// Remove this association
|
||||
invoker.unregisterPacketClass(proxyClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,6 @@ import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.server.NetLoginHandler;
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.bukkit.Server;
|
||||
@ -32,6 +31,7 @@ import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.ObjectCloner;
|
||||
import com.comphenix.protocol.reflect.VolatileField;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
/**
|
||||
* Used to ensure that the 1.3 server is referencing the correct server handler.
|
||||
@ -229,7 +229,7 @@ class InjectedServerConnection {
|
||||
@Override
|
||||
protected void onInserting(Object inserting) {
|
||||
// Ready for some login handler injection?
|
||||
if (inserting instanceof NetLoginHandler) {
|
||||
if (MinecraftReflection.isLoginHandler(inserting)) {
|
||||
Object replaced = netLoginInjector.onNetLoginCreated(inserting);
|
||||
|
||||
// Only replace if it has changed
|
||||
@ -241,7 +241,7 @@ class InjectedServerConnection {
|
||||
@Override
|
||||
protected void onRemoved(Object removing) {
|
||||
// Clean up?
|
||||
if (removing instanceof NetLoginHandler) {
|
||||
if (MinecraftReflection.isLoginHandler(removing)) {
|
||||
netLoginInjector.cleanup(removing);
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +40,6 @@ import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.VolatileField;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
|
||||
/**
|
||||
* Injection hook that overrides the packet queue lists in NetworkHandler.
|
||||
*
|
||||
@ -58,7 +56,7 @@ class NetworkFieldInjector extends PlayerInjector {
|
||||
}
|
||||
|
||||
// Packets to ignore
|
||||
private Set<Packet> ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap<Packet, Boolean>());
|
||||
private Set<Object> ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap<Object, Boolean>());
|
||||
|
||||
// Overridden fields
|
||||
private List<VolatileField> overridenLists = new ArrayList<VolatileField>();
|
||||
@ -99,7 +97,7 @@ class NetworkFieldInjector extends PlayerInjector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException {
|
||||
public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
|
||||
|
||||
if (networkManager != null) {
|
||||
try {
|
||||
@ -147,14 +145,14 @@ class NetworkFieldInjector extends PlayerInjector {
|
||||
VolatileField overwriter = new VolatileField(field, networkManager, true);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Packet> minecraftList = (List<Packet>) overwriter.getOldValue();
|
||||
List<Object> minecraftList = (List<Object>) overwriter.getOldValue();
|
||||
|
||||
synchronized(syncObject) {
|
||||
// The list we'll be inserting
|
||||
List<Packet> hackedList = new InjectedArrayList(classLoader, this, ignoredPackets);
|
||||
List<Object> hackedList = new InjectedArrayList(classLoader, this, ignoredPackets);
|
||||
|
||||
// Add every previously stored packet
|
||||
for (Packet packet : minecraftList) {
|
||||
for (Object packet : minecraftList) {
|
||||
hackedList.add(packet);
|
||||
}
|
||||
|
||||
@ -172,8 +170,8 @@ class NetworkFieldInjector extends PlayerInjector {
|
||||
protected void cleanHook() {
|
||||
// Clean up
|
||||
for (VolatileField overriden : overridenLists) {
|
||||
List<Packet> minecraftList = (List<Packet>) overriden.getOldValue();
|
||||
List<Packet> hacketList = (List<Packet>) overriden.getValue();
|
||||
List<Object> minecraftList = (List<Object>) overriden.getOldValue();
|
||||
List<Object> hacketList = (List<Object>) overriden.getValue();
|
||||
|
||||
if (minecraftList == hacketList) {
|
||||
return;
|
||||
@ -183,7 +181,7 @@ class NetworkFieldInjector extends PlayerInjector {
|
||||
synchronized(syncObject) {
|
||||
try {
|
||||
// Copy over current packets
|
||||
for (Packet packet : (List<Packet>) overriden.getValue()) {
|
||||
for (Object packet : (List<Object>) overriden.getValue()) {
|
||||
minecraftList.add(packet);
|
||||
}
|
||||
} finally {
|
||||
|
@ -19,7 +19,6 @@ package com.comphenix.protocol.injector.player;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
@ -67,7 +66,7 @@ class NetworkObjectInjector extends PlayerInjector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException {
|
||||
public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
|
||||
Object networkDelegate = filtered ? networkManagerRef.getValue() : networkManagerRef.getOldValue();
|
||||
|
||||
if (networkDelegate != null) {
|
||||
@ -114,7 +113,7 @@ class NetworkObjectInjector extends PlayerInjector {
|
||||
Callback queueFilter = new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
Packet packet = (Packet) args[0];
|
||||
Object packet = args[0];
|
||||
|
||||
if (packet != null) {
|
||||
packet = handlePacketSending(packet);
|
||||
|
@ -21,7 +21,6 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
@ -43,6 +42,7 @@ import com.comphenix.protocol.reflect.ObjectCloner;
|
||||
import com.comphenix.protocol.reflect.VolatileField;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.reflect.instances.ExistingGenerator;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
/**
|
||||
* Represents a player hook into the NetServerHandler class.
|
||||
@ -94,7 +94,7 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException {
|
||||
public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
|
||||
Object serverDeleage = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue();
|
||||
|
||||
if (serverDeleage != null) {
|
||||
@ -152,8 +152,7 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
Callback sendPacketCallback = new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
|
||||
Packet packet = (Packet) args[0];
|
||||
Object packet = args[0];
|
||||
|
||||
if (packet != null) {
|
||||
packet = handlePacketSending(packet);
|
||||
@ -237,7 +236,7 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
}
|
||||
|
||||
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
|
||||
if (clazz.getName().startsWith("net.minecraft.server."))
|
||||
if (clazz.getName().startsWith(MinecraftReflection.getMinecraftPackage()))
|
||||
return clazz;
|
||||
else if (clazz.equals(Object.class))
|
||||
return clazz;
|
||||
|
@ -26,8 +26,6 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@ -501,7 +499,7 @@ public class PlayerInjectionHandler {
|
||||
* @throws IllegalAccessException If the reflection machinery failed.
|
||||
* @throws InvocationTargetException If the underlying method caused an error.
|
||||
*/
|
||||
public void processPacket(Player player, Packet mcPacket) throws IllegalAccessException, InvocationTargetException {
|
||||
public void processPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
|
||||
|
||||
PlayerInjector injector = getInjector(player);
|
||||
|
||||
|
@ -25,12 +25,8 @@ import java.lang.reflect.Method;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import net.minecraft.server.EntityPlayer;
|
||||
import net.minecraft.server.NetLoginHandler;
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.Packets;
|
||||
@ -38,6 +34,7 @@ import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
@ -45,6 +42,7 @@ import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.VolatileField;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
abstract class PlayerInjector {
|
||||
|
||||
@ -122,9 +120,9 @@ abstract class PlayerInjector {
|
||||
* @param player - the player to retrieve.
|
||||
* @return Notch player object.
|
||||
*/
|
||||
protected EntityPlayer getEntityPlayer(Player player) {
|
||||
CraftPlayer craft = (CraftPlayer) player;
|
||||
return craft.getHandle();
|
||||
protected Object getEntityPlayer(Player player) {
|
||||
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
|
||||
return unwrapper.unwrapItem(player);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,7 +136,7 @@ abstract class PlayerInjector {
|
||||
//Dispatch to the correct injection method
|
||||
if (injectionSource instanceof Player)
|
||||
initializePlayer(injectionSource);
|
||||
else if (injectionSource instanceof NetLoginHandler)
|
||||
else if (MinecraftReflection.isLoginHandler(injectionSource))
|
||||
initializeLogin(injectionSource);
|
||||
else
|
||||
throw new IllegalArgumentException("Cannot initialize a player hook using a " + injectionSource.getClass().getName());
|
||||
@ -150,7 +148,7 @@ abstract class PlayerInjector {
|
||||
*/
|
||||
public void initializePlayer(Object player) {
|
||||
|
||||
EntityPlayer notchEntity = getEntityPlayer((Player) player);
|
||||
Object notchEntity = getEntityPlayer((Player) player);
|
||||
|
||||
if (!hasInitialized) {
|
||||
// Do this first, in case we encounter an exception
|
||||
@ -204,7 +202,7 @@ abstract class PlayerInjector {
|
||||
// And the queue method
|
||||
if (queueMethod == null)
|
||||
queueMethod = FuzzyReflection.fromClass(reference.getType()).
|
||||
getMethodByParameters("queue", Packet.class );
|
||||
getMethodByParameters("queue", MinecraftReflection.getPacketClass());
|
||||
|
||||
// And the data input stream that we'll use to identify a player
|
||||
if (inputField == null)
|
||||
@ -323,7 +321,7 @@ abstract class PlayerInjector {
|
||||
}
|
||||
}
|
||||
|
||||
private Field getProxyField(EntityPlayer notchEntity, Field serverField) {
|
||||
private Field getProxyField(Object notchEntity, Field serverField) {
|
||||
|
||||
try {
|
||||
Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true);
|
||||
@ -379,7 +377,7 @@ abstract class PlayerInjector {
|
||||
try {
|
||||
// Well, that sucks. Try just Minecraft objects then.
|
||||
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
|
||||
getFieldByType(FuzzyReflection.MINECRAFT_OBJECT);
|
||||
getFieldByType(MinecraftReflection.MINECRAFT_OBJECT);
|
||||
|
||||
} catch (RuntimeException e2) {
|
||||
throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage());
|
||||
@ -398,10 +396,10 @@ abstract class PlayerInjector {
|
||||
* @return The stored entity player.
|
||||
* @throws IllegalAccessException If the reflection failed.
|
||||
*/
|
||||
private EntityPlayer getEntityPlayer(Object netHandler) throws IllegalAccessException {
|
||||
private Object getEntityPlayer(Object netHandler) throws IllegalAccessException {
|
||||
if (entityPlayerField == null)
|
||||
entityPlayerField = FuzzyReflection.fromObject(netHandler).getFieldByType(".*EntityPlayer");
|
||||
return (EntityPlayer) FieldUtils.readField(entityPlayerField, netHandler);
|
||||
return FieldUtils.readField(entityPlayerField, netHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -410,15 +408,15 @@ abstract class PlayerInjector {
|
||||
* @throws IllegalAccessException If the reflection machinery failed.
|
||||
* @throws InvocationTargetException If the underlying method caused an error.
|
||||
*/
|
||||
public void processPacket(Packet packet) throws IllegalAccessException, InvocationTargetException {
|
||||
public void processPacket(Object packet) throws IllegalAccessException, InvocationTargetException {
|
||||
|
||||
Object netHandler = getNetHandler();
|
||||
|
||||
// Get the process method
|
||||
if (processMethod == null) {
|
||||
try {
|
||||
processMethod = FuzzyReflection.fromClass(Packet.class).
|
||||
getMethodByParameters("processPacket", netHandlerField.getType());
|
||||
processMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
|
||||
getMethodByParameters("processPacket", netHandlerField.getType());
|
||||
} catch (RuntimeException e) {
|
||||
throw new IllegalArgumentException("Cannot locate process packet method: " + e.getMessage());
|
||||
}
|
||||
@ -440,7 +438,7 @@ abstract class PlayerInjector {
|
||||
* @param filtered - whether or not the packet will be filtered by our listeners.
|
||||
* @param InvocationTargetException If an error occured when sending the packet.
|
||||
*/
|
||||
public abstract void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException;
|
||||
public abstract void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Inject a hook to catch packets sent to the current player.
|
||||
@ -501,7 +499,7 @@ abstract class PlayerInjector {
|
||||
* @param packet - packet to sent.
|
||||
* @return The given packet, or the packet replaced by the listeners.
|
||||
*/
|
||||
public Packet handlePacketSending(Packet packet) {
|
||||
public Object handlePacketSending(Object packet) {
|
||||
try {
|
||||
// Get the packet ID too
|
||||
Integer id = invoker.getPacketID(packet);
|
||||
@ -516,7 +514,7 @@ abstract class PlayerInjector {
|
||||
if (updateOnLogin) {
|
||||
if (id == Packets.Server.LOGIN) {
|
||||
try {
|
||||
updatedPlayer = getEntityPlayer(getNetHandler()).getBukkitEntity();
|
||||
updatedPlayer = (Player) MinecraftReflection.getBukkitEntity(getEntityPlayer(getNetHandler()));
|
||||
} catch (IllegalAccessException e) {
|
||||
reporter.reportDetailed(this, "Cannot update player in PlayerEvent.", e, packet);
|
||||
}
|
||||
|
@ -234,7 +234,8 @@ class Metrics {
|
||||
*
|
||||
* @return True if statistics measuring is running, otherwise false.
|
||||
*/
|
||||
public boolean start() {
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean start() {
|
||||
synchronized (optOutLock) {
|
||||
// Did we opt out?
|
||||
if (isOptOut()) {
|
||||
|
@ -32,11 +32,6 @@ import java.util.regex.Pattern;
|
||||
* @author Kristian
|
||||
*/
|
||||
public class FuzzyReflection {
|
||||
|
||||
/**
|
||||
* Matches a Minecraft object.
|
||||
*/
|
||||
public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+";
|
||||
|
||||
// The class we're actually representing
|
||||
private Class<?> source;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,463 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
|
||||
/**
|
||||
* Methods and constants specifically used in conjuction with reflecting Minecraft object.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class MinecraftReflection {
|
||||
/**
|
||||
* Regular expression that matches a Minecraft object.
|
||||
*/
|
||||
public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+";
|
||||
|
||||
/**
|
||||
* The package name of all the classes that belongs to the native code in Minecraft.
|
||||
*/
|
||||
private static String MINECRAFT_PREFIX_PACKAGE = "net.minecraft.server";
|
||||
|
||||
private static String MINECRAFT_FULL_PACKAGE = null;
|
||||
private static String CRAFTBUKKIT_PACKAGE = null;
|
||||
|
||||
private static CachedPackage minecraftPackage;
|
||||
private static CachedPackage craftbukkitPackage;
|
||||
|
||||
// org.bukkit.craftbukkit
|
||||
private static Class<?> craftItemStackClass;
|
||||
private static Constructor<?> craftNMSConstructor;
|
||||
private static Constructor<?> craftBukkitConstructor;
|
||||
|
||||
// net.minecraft.server
|
||||
private static Class<?> itemStackArrayClass;
|
||||
|
||||
/**
|
||||
* Retrieve the name of the Minecraft server package.
|
||||
* @return Full canonical name of the Minecraft server package.
|
||||
*/
|
||||
public static String getMinecraftPackage() {
|
||||
// Speed things up
|
||||
if (MINECRAFT_FULL_PACKAGE != null)
|
||||
return MINECRAFT_FULL_PACKAGE;
|
||||
|
||||
Server craftServer = Bukkit.getServer();
|
||||
|
||||
// This server should have a "getHandle" method that we can use
|
||||
if (craftServer != null) {
|
||||
try {
|
||||
Class<?> craftClass = craftServer.getClass();
|
||||
Method getHandle = craftClass.getMethod("getHandle");
|
||||
|
||||
Class<?> returnType = getHandle.getReturnType();
|
||||
String returnName = returnType.getCanonicalName();
|
||||
|
||||
// The return type will tell us the full package, regardless of formating
|
||||
CRAFTBUKKIT_PACKAGE = getPackage(craftClass.getCanonicalName());
|
||||
MINECRAFT_FULL_PACKAGE = getPackage(returnName);
|
||||
return MINECRAFT_FULL_PACKAGE;
|
||||
|
||||
} catch (SecurityException e) {
|
||||
throw new RuntimeException("Security violation. Cannot get handle method.", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalStateException("Cannot find getHandle() method on server. Is this a modified CraftBukkit version?", e);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("Could not find Bukkit. Is it running?");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used during debugging and testing.
|
||||
* @param minecraftPackage - the current Minecraft package.
|
||||
* @param craftBukkitPackage - the current CraftBukkit package.
|
||||
*/
|
||||
public static void setMinecraftPackage(String minecraftPackage, String craftBukkitPackage) {
|
||||
MINECRAFT_FULL_PACKAGE = minecraftPackage;
|
||||
CRAFTBUKKIT_PACKAGE = craftBukkitPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the name of the root CraftBukkit package.
|
||||
* @return Full canonical name of the root CraftBukkit package.
|
||||
*/
|
||||
public static String getCraftBukkitPackage() {
|
||||
// Ensure it has been initialized
|
||||
getMinecraftPackage();
|
||||
return CRAFTBUKKIT_PACKAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the package name from a given canonical Java class name.
|
||||
* @param fullName - full Java class name.
|
||||
* @return The package name.
|
||||
*/
|
||||
private static String getPackage(String fullName) {
|
||||
return fullName.substring(0, fullName.lastIndexOf("."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given object can be found within the package net.minecraft.server.
|
||||
* @param obj - the object to test.
|
||||
* @return TRUE if it can, FALSE otherwise.
|
||||
*/
|
||||
public static boolean isMinecraftObject(@Nonnull Object obj) {
|
||||
if (obj == null)
|
||||
throw new IllegalArgumentException("Cannot determine the type of a null object.");
|
||||
|
||||
// Doesn't matter if we don't check for the version here
|
||||
return obj.getClass().getName().startsWith(MINECRAFT_PREFIX_PACKAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given object is found in net.minecraft.server, and has the given name.
|
||||
* @param obj - the object to test.
|
||||
* @param className - the class name to test.
|
||||
* @return TRUE if it can, FALSE otherwise.
|
||||
*/
|
||||
public static boolean isMinecraftObject(@Nonnull Object obj, String className) {
|
||||
if (obj == null)
|
||||
throw new IllegalArgumentException("Cannot determine the type of a null object.");
|
||||
|
||||
String javaName = obj.getClass().getName();
|
||||
return javaName.startsWith(MINECRAFT_PREFIX_PACKAGE) && javaName.endsWith(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically retrieve the Bukkit entity from a given entity.
|
||||
* @param nmsObject - the NMS entity.
|
||||
* @return A bukkit entity.
|
||||
* @throws RuntimeException If we were unable to retrieve the Bukkit entity.
|
||||
*/
|
||||
public static Object getBukkitEntity(Object nmsObject) {
|
||||
if (nmsObject == null)
|
||||
return null;
|
||||
|
||||
// We will have to do this dynamically, unfortunately
|
||||
try {
|
||||
return nmsObject.getClass().getMethod("getBukkitEntity").invoke(nmsObject);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot get Bukkit entity from " + nmsObject, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given object is a ChunkPosition.
|
||||
* @param obj - the object to test.
|
||||
* @return TRUE if it can, FALSE otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isChunkPosition(Object obj) {
|
||||
return getChunkPositionClass().isAssignableFrom(obj.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given object is a ChunkCoordinate.
|
||||
* @param obj - the object to test.
|
||||
* @return TRUE if it can, FALSE otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isChunkCoordinates(Object obj) {
|
||||
return getChunkCoordinatesClass().isAssignableFrom(obj.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given object is actually a Minecraft packet.
|
||||
* @param obj - the given object.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isPacketClass(Object obj) {
|
||||
return getPacketClass().isAssignableFrom(obj.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given object is a NetLoginHandler.
|
||||
* @param obj - the given object.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isLoginHandler(Object obj) {
|
||||
return getNetLoginHandlerClass().isAssignableFrom(obj.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given object is actually a Minecraft packet.
|
||||
* @param obj - the given object.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isMinecraftEntity(Object obj) {
|
||||
return getEntityClass().isAssignableFrom(obj.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given object is a NMS ItemStack.
|
||||
* @param obj - the given object.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isItemStack(Object value) {
|
||||
return getItemStackClass().isAssignableFrom(value.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given object is a Minecraft player entity.
|
||||
* @param obj - the given object.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isMinecraftPlayer(Object obj) {
|
||||
return getEntityPlayerClass().isAssignableFrom(obj.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given object is a watchable object.
|
||||
* @param obj - the given object.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isWatchableObject(Object obj) {
|
||||
return getWatchableObjectClass().isAssignableFrom(obj.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given object is a data watcher object.
|
||||
* @param obj - the given object.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isDataWatcher(Object obj) {
|
||||
return getDataWatcherClass().isAssignableFrom(obj.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given object is a CraftItemStack instancey.
|
||||
* @param obj - the given object.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isCraftItemStack(Object obj) {
|
||||
return getCraftItemStackClass().isAssignableFrom(obj.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the EntityPlayer (NMS) class.
|
||||
* @return The entity class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getEntityPlayerClass() {
|
||||
return getMinecraftClass("EntityPlayer");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the entity (NMS) class.
|
||||
* @return The entity class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getEntityClass() {
|
||||
return getMinecraftClass("Entity");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the packet class.
|
||||
* @return The packet class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getPacketClass() {
|
||||
return getMinecraftClass("Packet");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the NetLoginHandler class.
|
||||
* @return The NetLoginHandler class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getNetLoginHandlerClass() {
|
||||
return getMinecraftClass("NetLoginHandler");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the NetLoginHandler class.
|
||||
* @return The NetLoginHandler class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getItemStackClass() {
|
||||
return getMinecraftClass("ItemStack");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the WorldType class.
|
||||
* @return The WorldType class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getWorldTypeClass() {
|
||||
return getMinecraftClass("WorldType");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the DataWatcher class.
|
||||
* @return The DataWatcher class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getDataWatcherClass() {
|
||||
return getMinecraftClass("DataWatcher");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the ChunkPosition class.
|
||||
* @return The ChunkPosition class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getChunkPositionClass() {
|
||||
return getMinecraftClass("ChunkPosition");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the ChunkPosition class.
|
||||
* @return The ChunkPosition class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getChunkCoordinatesClass() {
|
||||
return getMinecraftClass("ChunkCoordinates");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the WatchableObject class.
|
||||
* @return The WatchableObject class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getWatchableObjectClass() {
|
||||
return getMinecraftClass("WatchableObject");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the ItemStack[] class.
|
||||
* @return The ItemStack[] class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getItemStackArrayClass() {
|
||||
if (itemStackArrayClass == null)
|
||||
itemStackArrayClass = getArrayClass(getItemStackClass());
|
||||
return itemStackArrayClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the array class of a given component type.
|
||||
* @param componentType - type of each element in the array.
|
||||
* @return The class of the array.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getArrayClass(Class componentType) {
|
||||
// Bit of a hack, but it works
|
||||
return Array.newInstance(componentType, 0).getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the CraftItemStack class.
|
||||
* @return The CraftItemStack class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getCraftItemStackClass() {
|
||||
if (craftItemStackClass == null)
|
||||
craftItemStackClass = getCraftBukkitClass("inventory.CraftItemStack");
|
||||
return craftItemStackClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a CraftItemStack from a given ItemStack.
|
||||
* @param bukkitItemStack - the Bukkit ItemStack to convert.
|
||||
* @return A CraftItemStack as an ItemStack.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static ItemStack getBukkitItemStack(ItemStack bukkitItemStack) {
|
||||
if (craftBukkitConstructor == null) {
|
||||
try {
|
||||
craftBukkitConstructor = getCraftItemStackClass().getConstructor(ItemStack.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot find CraftItemStack(org.bukkit.inventory.ItemStack).", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to create the CraftItemStack
|
||||
try {
|
||||
return (ItemStack) craftBukkitConstructor.newInstance(bukkitItemStack);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot construct CraftItemStack.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Bukkit ItemStack from a given net.minecraft.server ItemStack.
|
||||
* @param minecraftItemStack - the NMS ItemStack to wrap.
|
||||
* @return The wrapped ItemStack.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static ItemStack getBukkitItemStack(Object minecraftItemStack) {
|
||||
if (craftNMSConstructor == null) {
|
||||
try {
|
||||
craftNMSConstructor = getCraftItemStackClass().getConstructor(minecraftItemStack.getClass());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot find CraftItemStack(net.mineraft.server.ItemStack).", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to create the CraftItemStack
|
||||
try {
|
||||
return (ItemStack) craftNMSConstructor.newInstance(minecraftItemStack);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot construct CraftItemStack.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the net.minecraft.server ItemStack from a Bukkit ItemStack.
|
||||
* @param stack - the Bukkit ItemStack to convert.
|
||||
* @return The NMS ItemStack.
|
||||
*/
|
||||
public static Object getMinecraftItemStack(ItemStack stack) {
|
||||
// Make sure this is a CraftItemStack
|
||||
if (!isCraftItemStack(stack))
|
||||
stack = getBukkitItemStack(stack);
|
||||
|
||||
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
|
||||
return unwrapper.unwrapItem(stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the class object of a specific CraftBukkit class.
|
||||
* @param className - the specific CraftBukkit class.
|
||||
* @return Class object.
|
||||
* @throws RuntimeException If we are unable to find the given class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getCraftBukkitClass(String className) {
|
||||
if (craftbukkitPackage == null)
|
||||
craftbukkitPackage = new CachedPackage(getCraftBukkitPackage());
|
||||
return craftbukkitPackage.getPackageClass(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the class object of a specific Minecraft class.
|
||||
* @param className - the specific Minecraft class.
|
||||
* @return Class object.
|
||||
* @throws RuntimeException If we are unable to find the given class.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class getMinecraftClass(String className) {
|
||||
if (minecraftPackage == null)
|
||||
minecraftPackage = new CachedPackage(getMinecraftPackage());
|
||||
return minecraftPackage.getPackageClass(className);
|
||||
}
|
||||
}
|
@ -1,16 +1,13 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.server.DataWatcher;
|
||||
import net.minecraft.server.WatchableObject;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.WorldType;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
@ -19,6 +16,7 @@ import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
/**
|
||||
* Contains several useful equivalent converters for normal Bukkit types.
|
||||
@ -29,9 +27,13 @@ public class BukkitConverters {
|
||||
// Check whether or not certain classes exists
|
||||
private static boolean hasWorldType = false;
|
||||
|
||||
// Used to access the world type
|
||||
private static Method worldTypeName;
|
||||
private static Method worldTypeGetType;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("net.minecraft.server.WorldType");
|
||||
Class.forName(MinecraftReflection.getMinecraftPackage() + ".WorldType");
|
||||
hasWorldType = true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
}
|
||||
@ -100,8 +102,8 @@ public class BukkitConverters {
|
||||
}
|
||||
|
||||
public WrappedWatchableObject getSpecific(Object generic) {
|
||||
if (generic instanceof WatchableObject)
|
||||
return new WrappedWatchableObject((WatchableObject) generic);
|
||||
if (MinecraftReflection.isWatchableObject(generic))
|
||||
return new WrappedWatchableObject(generic);
|
||||
else if (generic instanceof WrappedWatchableObject)
|
||||
return (WrappedWatchableObject) generic;
|
||||
else
|
||||
@ -128,8 +130,8 @@ public class BukkitConverters {
|
||||
|
||||
@Override
|
||||
public WrappedDataWatcher getSpecific(Object generic) {
|
||||
if (generic instanceof DataWatcher)
|
||||
return new WrappedDataWatcher((DataWatcher) generic);
|
||||
if (MinecraftReflection.isDataWatcher(generic))
|
||||
return new WrappedDataWatcher(generic);
|
||||
else if (generic instanceof WrappedDataWatcher)
|
||||
return (WrappedDataWatcher) generic;
|
||||
else
|
||||
@ -153,15 +155,35 @@ public class BukkitConverters {
|
||||
return null;
|
||||
|
||||
return getIgnoreNull(new EquivalentConverter<WorldType>() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, WorldType specific) {
|
||||
return net.minecraft.server.WorldType.getType(specific.getName());
|
||||
try {
|
||||
if (worldTypeGetType == null)
|
||||
worldTypeGetType = MinecraftReflection.getWorldTypeClass().getMethod("getType", String.class);
|
||||
|
||||
// Convert to the Bukkit world type
|
||||
return worldTypeGetType.invoke(this, specific.getName());
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new FieldAccessException("Cannot find the WorldType.getType() method.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public WorldType getSpecific(Object generic) {
|
||||
net.minecraft.server.WorldType type = (net.minecraft.server.WorldType) generic;
|
||||
return WorldType.getByName(type.name());
|
||||
try {
|
||||
if (worldTypeName == null)
|
||||
worldTypeName = MinecraftReflection.getWorldTypeClass().getMethod("name");
|
||||
|
||||
// Dynamically call the namne method
|
||||
String name = (String) worldTypeName.invoke(generic);
|
||||
return WorldType.getByName(name);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new FieldAccessException("Cannot call the name method in WorldType.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -221,12 +243,12 @@ public class BukkitConverters {
|
||||
public static EquivalentConverter<ItemStack> getItemStackConverter() {
|
||||
return getIgnoreNull(new EquivalentConverter<ItemStack>() {
|
||||
public Object getGeneric(Class<?> genericType, ItemStack specific) {
|
||||
return toStackNMS(specific);
|
||||
return MinecraftReflection.getMinecraftItemStack(specific);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getSpecific(Object generic) {
|
||||
return new CraftItemStack((net.minecraft.server.ItemStack) generic);
|
||||
return MinecraftReflection.getBukkitItemStack(generic);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -236,20 +258,6 @@ public class BukkitConverters {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an item stack to the NMS equivalent.
|
||||
* @param stack - Bukkit stack to convert.
|
||||
* @return A bukkit stack.
|
||||
*/
|
||||
private static net.minecraft.server.ItemStack toStackNMS(ItemStack stack) {
|
||||
// We must be prepared for an object that simply implements ItemStcak
|
||||
if (stack instanceof CraftItemStack) {
|
||||
return ((CraftItemStack) stack).getHandle();
|
||||
} else {
|
||||
return (new CraftItemStack(stack)).getHandle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a given equivalent converter in NULL checks, ensuring that such values are ignored.
|
||||
* @param delegate - the underlying equivalent converter.
|
||||
|
@ -1,10 +1,13 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
@ -19,6 +22,8 @@ public class ChunkPosition {
|
||||
*/
|
||||
public static ChunkPosition ORIGIN = new ChunkPosition(0, 0, 0);
|
||||
|
||||
private static Constructor<?> chunkPositionConstructor;
|
||||
|
||||
// Use protected members, like Bukkit
|
||||
protected final int x;
|
||||
protected final int y;
|
||||
@ -128,27 +133,35 @@ public class ChunkPosition {
|
||||
*/
|
||||
public static EquivalentConverter<ChunkPosition> getConverter() {
|
||||
return new EquivalentConverter<ChunkPosition>() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, ChunkPosition specific) {
|
||||
return new net.minecraft.server.ChunkPosition(specific.x, specific.y, specific.z);
|
||||
if (chunkPositionConstructor == null) {
|
||||
try {
|
||||
chunkPositionConstructor = MinecraftReflection.getChunkPositionClass().
|
||||
getConstructor(int.class, int.class, int.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot find chunk position constructor.", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the underlying ChunkPosition
|
||||
try {
|
||||
return chunkPositionConstructor.newInstance(specific.x, specific.y, specific.z);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot construct ChunkPosition.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkPosition getSpecific(Object generic) {
|
||||
if (generic instanceof net.minecraft.server.ChunkPosition) {
|
||||
net.minecraft.server.ChunkPosition other = (net.minecraft.server.ChunkPosition) generic;
|
||||
if (MinecraftReflection.isChunkPosition(generic)) {
|
||||
// Use a structure modifier
|
||||
intModifier = new StructureModifier<Object>(generic.getClass(), null, false).withType(int.class);
|
||||
|
||||
try {
|
||||
if (intModifier == null)
|
||||
return new ChunkPosition(other.x, other.y, other.z);
|
||||
} catch (LinkageError e) {
|
||||
// It could happen. If it does, use a structure modifier instead
|
||||
intModifier = new StructureModifier<Object>(other.getClass(), null, false).withType(int.class);
|
||||
|
||||
// Damn it all
|
||||
if (intModifier.size() < 3) {
|
||||
throw new IllegalStateException("Cannot read class " + other.getClass() + " for its integer fields.");
|
||||
}
|
||||
// Damn it all
|
||||
if (intModifier.size() < 3) {
|
||||
throw new IllegalStateException("Cannot read class " + generic.getClass() + " for its integer fields.");
|
||||
}
|
||||
|
||||
if (intModifier.size() >= 3) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import net.minecraft.server.ChunkCoordinates;
|
||||
|
||||
/**
|
||||
* Allows access to a chunk coordinate.
|
||||
*
|
||||
@ -16,23 +16,42 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
|
||||
*/
|
||||
private static final boolean LARGER_THAN_NULL = true;
|
||||
|
||||
protected ChunkCoordinates handle;
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected Comparable handle;
|
||||
|
||||
// Used to access a ChunkCoordinate
|
||||
private static StructureModifier<Integer> intModifier;
|
||||
|
||||
/**
|
||||
* Create a new empty wrapper.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public WrappedChunkCoordinate() {
|
||||
this(new ChunkCoordinates());
|
||||
try {
|
||||
this.handle = (Comparable) MinecraftReflection.getChunkCoordinatesClass().newInstance();
|
||||
initializeModifier();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot construct chunk coordinate.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapper for a specific chunk coordinates.
|
||||
* @param handle - the NMS chunk coordinates.
|
||||
*/
|
||||
public WrappedChunkCoordinate(ChunkCoordinates handle) {
|
||||
@SuppressWarnings("rawtypes")
|
||||
public WrappedChunkCoordinate(Comparable handle) {
|
||||
if (handle == null)
|
||||
throw new IllegalArgumentException("handle cannot be NULL");
|
||||
this.handle = handle;
|
||||
initializeModifier();
|
||||
}
|
||||
|
||||
// Ensure that the structure modifier is initialized
|
||||
private void initializeModifier() {
|
||||
if (intModifier == null) {
|
||||
intModifier = new StructureModifier<Object>(handle.getClass(), null, false).withType(int.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,7 +75,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
|
||||
this(position.getX(), position.getY(), position.getZ());
|
||||
}
|
||||
|
||||
public ChunkCoordinates getHandle() {
|
||||
public Object getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -65,7 +84,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
|
||||
* @return The x coordinate.
|
||||
*/
|
||||
public int getX() {
|
||||
return handle.x;
|
||||
return intModifier.read(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,7 +92,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
|
||||
* @param newX - the new x coordinate.
|
||||
*/
|
||||
public void setX(int newX) {
|
||||
handle.x = newX;
|
||||
intModifier.write(0, newX);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,7 +100,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
|
||||
* @return The y coordinate.
|
||||
*/
|
||||
public int getY() {
|
||||
return handle.y;
|
||||
return intModifier.read(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,7 +108,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
|
||||
* @param newY - the new y coordinate.
|
||||
*/
|
||||
public void setY(int newY) {
|
||||
handle.y = newY;
|
||||
intModifier.write(1, newY);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,7 +116,15 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
|
||||
* @return The z coordinate.
|
||||
*/
|
||||
public int getZ() {
|
||||
return handle.z;
|
||||
return intModifier.read(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the z coordinate of the underlying coordiate.
|
||||
* @param newZ - the new z coordinate.
|
||||
*/
|
||||
public void setZ(int newZ) {
|
||||
intModifier.write(2, newZ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,14 +135,7 @@ public class WrappedChunkCoordinate implements Comparable<WrappedChunkCoordinate
|
||||
return new ChunkPosition(getX(), getY(), getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the z coordinate of the underlying coordiate.
|
||||
* @param newZ - the new z coordinate.
|
||||
*/
|
||||
public void setZ(int newZ) {
|
||||
handle.z = newZ;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public int compareTo(WrappedChunkCoordinate other) {
|
||||
// We'll handle NULL objects too, unlike ChunkCoordinates
|
||||
|
@ -6,6 +6,7 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -13,6 +14,8 @@ import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
@ -20,17 +23,17 @@ import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import net.minecraft.server.DataWatcher;
|
||||
import net.minecraft.server.WatchableObject;
|
||||
import com.google.common.collect.Iterators;
|
||||
|
||||
/**
|
||||
* Wraps a DataWatcher that is used to transmit arbitrary key-value pairs with a given entity.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class WrappedDataWatcher {
|
||||
public class WrappedDataWatcher implements Iterable<WrappedWatchableObject> {
|
||||
|
||||
/**
|
||||
* Used to assign integer IDs to given types.
|
||||
@ -55,7 +58,7 @@ public class WrappedDataWatcher {
|
||||
private static boolean hasInitialized;
|
||||
|
||||
// The underlying DataWatcher we're modifying
|
||||
protected DataWatcher handle;
|
||||
protected Object handle;
|
||||
|
||||
// Lock
|
||||
private ReadWriteLock readWriteLock;
|
||||
@ -69,7 +72,13 @@ public class WrappedDataWatcher {
|
||||
*/
|
||||
public WrappedDataWatcher() {
|
||||
// Just create a new watcher
|
||||
this(new DataWatcher());
|
||||
try {
|
||||
this.handle = MinecraftReflection.getDataWatcherClass().newInstance();
|
||||
initialize();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to construct DataWatcher.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,27 +86,42 @@ public class WrappedDataWatcher {
|
||||
* @param handle - the data watcher to wrap.
|
||||
* @throws FieldAccessException If we're unable to wrap a DataWatcher.
|
||||
*/
|
||||
public WrappedDataWatcher(DataWatcher handle) {
|
||||
this.handle = handle;
|
||||
public WrappedDataWatcher(Object handle) {
|
||||
if (handle == null)
|
||||
throw new IllegalArgumentException("Handle cannot be NULL.");
|
||||
if (!MinecraftReflection.isDataWatcher(handle))
|
||||
throw new IllegalArgumentException("The value " + handle + " is not a DataWatcher.");
|
||||
|
||||
try {
|
||||
initialize();
|
||||
} catch (FieldAccessException e) {
|
||||
throw new RuntimeException("Cannot initialize wrapper.", e);
|
||||
}
|
||||
this.handle = handle;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new data watcher from a list of watchable objects.
|
||||
* Create a new data watcher for a list of watchable objects.
|
||||
* <p>
|
||||
* Note that the watchable objects are not cloned, and will be modified in place. Use "deepClone" if
|
||||
* that is not desirable.
|
||||
* <p>
|
||||
* The {@link #removeObject(int)} method will not modify the given list, however.
|
||||
*
|
||||
* @param watchableObjects - list of watchable objects that will be copied.
|
||||
* @throws FieldAccessException Unable to read watchable objects.
|
||||
*/
|
||||
public WrappedDataWatcher(List<WrappedWatchableObject> watchableObjects) throws FieldAccessException {
|
||||
this();
|
||||
|
||||
Lock writeLock = getReadWriteLock().writeLock();
|
||||
Map<Integer, Object> map = getWatchableObjectMap();
|
||||
|
||||
// Fill the underlying map
|
||||
for (WrappedWatchableObject watched : watchableObjects) {
|
||||
setObject(watched.getIndex(), watched.getValue());
|
||||
writeLock.lock();
|
||||
|
||||
try {
|
||||
// Add the watchable objects by reference
|
||||
for (WrappedWatchableObject watched : watchableObjects) {
|
||||
map.put(watched.getIndex(), watched.handle);
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,7 +129,7 @@ public class WrappedDataWatcher {
|
||||
* Retrieves the underlying data watcher.
|
||||
* @return The underlying data watcher.
|
||||
*/
|
||||
public DataWatcher getHandle() {
|
||||
public Object getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -215,7 +239,7 @@ public class WrappedDataWatcher {
|
||||
*/
|
||||
public Object getObject(int index) throws FieldAccessException {
|
||||
// The get method will take care of concurrency
|
||||
WatchableObject watchable = getWatchedObject(index);
|
||||
Object watchable = getWatchedObject(index);
|
||||
|
||||
if (watchable != null) {
|
||||
return new WrappedWatchableObject(watchable).getValue();
|
||||
@ -230,15 +254,16 @@ public class WrappedDataWatcher {
|
||||
* @throws FieldAccessException If reflection failed.
|
||||
*/
|
||||
public List<WrappedWatchableObject> getWatchableObjects() throws FieldAccessException {
|
||||
Lock readLock = getReadWriteLock().readLock();
|
||||
readLock.lock();
|
||||
|
||||
try {
|
||||
getReadWriteLock().readLock().lock();
|
||||
|
||||
List<WrappedWatchableObject> result = new ArrayList<WrappedWatchableObject>();
|
||||
|
||||
// Add each watchable object to the list
|
||||
for (Object watchable : getWatchableObjectMap().values()) {
|
||||
if (watchable != null) {
|
||||
result.add(new WrappedWatchableObject((WatchableObject) watchable));
|
||||
result.add(new WrappedWatchableObject(watchable));
|
||||
} else {
|
||||
result.add(null);
|
||||
}
|
||||
@ -246,7 +271,7 @@ public class WrappedDataWatcher {
|
||||
return result;
|
||||
|
||||
} finally {
|
||||
getReadWriteLock().readLock().unlock();
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,6 +291,20 @@ public class WrappedDataWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone the content of the current DataWatcher.
|
||||
* @return A cloned data watcher.
|
||||
*/
|
||||
public WrappedDataWatcher deepClone() {
|
||||
WrappedDataWatcher clone = new WrappedDataWatcher();
|
||||
|
||||
// Make a new copy instead
|
||||
for (WrappedWatchableObject watchable : this) {
|
||||
clone.setObject(watchable.getIndex(), watchable.getClonedValue());
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of watched objects.
|
||||
* @return Watched object count.
|
||||
@ -282,6 +321,23 @@ public class WrappedDataWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a given object from the underlying DataWatcher.
|
||||
* @param index - index of the object to remove.
|
||||
* @return The watchable object that was removed, or NULL If none could be found.
|
||||
*/
|
||||
public WrappedWatchableObject removeObject(int index) {
|
||||
Lock writeLock = getReadWriteLock().writeLock();
|
||||
writeLock.lock();
|
||||
|
||||
try {
|
||||
Object removed = getWatchableObjectMap().remove(index);
|
||||
return removed != null ? new WrappedWatchableObject(removed) : null;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a watched byte.
|
||||
* @param index - index of the watched byte.
|
||||
@ -305,7 +361,7 @@ public class WrappedDataWatcher {
|
||||
writeLock.lock();
|
||||
|
||||
try {
|
||||
WatchableObject watchable = getWatchedObject(index);
|
||||
Object watchable = getWatchedObject(index);
|
||||
|
||||
if (watchable != null) {
|
||||
new WrappedWatchableObject(watchable).setValue(newValue, update);
|
||||
@ -325,18 +381,18 @@ public class WrappedDataWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
private WatchableObject getWatchedObject(int index) throws FieldAccessException {
|
||||
private Object getWatchedObject(int index) throws FieldAccessException {
|
||||
// We use the get-method first and foremost
|
||||
if (getKeyValueMethod != null) {
|
||||
try {
|
||||
return (WatchableObject) getKeyValueMethod.invoke(handle, index);
|
||||
return getKeyValueMethod.invoke(handle, index);
|
||||
} catch (Exception e) {
|
||||
throw new FieldAccessException("Cannot invoke get key method for index " + index, e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
getReadWriteLock().readLock().lock();
|
||||
return (WatchableObject) getWatchableObjectMap().get(index);
|
||||
return getWatchableObjectMap().get(index);
|
||||
|
||||
} finally {
|
||||
getReadWriteLock().readLock().unlock();
|
||||
@ -388,8 +444,8 @@ public class WrappedDataWatcher {
|
||||
*/
|
||||
public static WrappedDataWatcher getEntityWatcher(Entity entity) throws FieldAccessException {
|
||||
if (entityDataField == null)
|
||||
entityDataField = FuzzyReflection.fromClass(net.minecraft.server.Entity.class, true).
|
||||
getFieldByType("datawatcher", DataWatcher.class);
|
||||
entityDataField = FuzzyReflection.fromClass(MinecraftReflection.getEntityClass(), true).
|
||||
getFieldByType("datawatcher", MinecraftReflection.getDataWatcherClass());
|
||||
|
||||
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
|
||||
|
||||
@ -397,7 +453,7 @@ public class WrappedDataWatcher {
|
||||
Object nsmWatcher = FieldUtils.readField(entityDataField, unwrapper.unwrapItem(entity), true);
|
||||
|
||||
if (nsmWatcher != null)
|
||||
return new WrappedDataWatcher((DataWatcher) nsmWatcher);
|
||||
return new WrappedDataWatcher(nsmWatcher);
|
||||
else
|
||||
return null;
|
||||
|
||||
@ -417,7 +473,7 @@ public class WrappedDataWatcher {
|
||||
else
|
||||
return;
|
||||
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(DataWatcher.class, true);
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getDataWatcherClass(), true);
|
||||
|
||||
for (Field lookup : fuzzy.getFieldListByType(Map.class)) {
|
||||
if (Modifier.isStatic(lookup.getModifiers())) {
|
||||
@ -476,4 +532,20 @@ public class WrappedDataWatcher {
|
||||
// Use fallback method
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<WrappedWatchableObject> iterator() {
|
||||
// We'll wrap the iterator instead of creating a new list every time
|
||||
return Iterators.transform(getWatchableObjectMap().values().iterator(),
|
||||
new Function<Object, WrappedWatchableObject>() {
|
||||
|
||||
@Override
|
||||
public WrappedWatchableObject apply(@Nullable Object item) {
|
||||
if (item != null)
|
||||
return new WrappedWatchableObject(item);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
|
||||
import net.minecraft.server.ChunkCoordinates;
|
||||
import net.minecraft.server.ItemStack;
|
||||
import net.minecraft.server.WatchableObject;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* Represents a watchable object.
|
||||
@ -22,7 +24,13 @@ public class WrappedWatchableObject {
|
||||
// The field containing the value itself
|
||||
private static StructureModifier<Object> baseModifier;
|
||||
|
||||
protected WatchableObject handle;
|
||||
// Used to create new watchable objects
|
||||
private static Constructor<?> watchableConstructor;
|
||||
|
||||
// The watchable object class type
|
||||
private static Class<?> watchableObjectClass;
|
||||
|
||||
protected Object handle;
|
||||
protected StructureModifier<Object> modifier;
|
||||
|
||||
// Type of the stored value
|
||||
@ -32,7 +40,7 @@ public class WrappedWatchableObject {
|
||||
* Wrap a given raw Minecraft watchable object.
|
||||
* @param handle - the raw watchable object to wrap.
|
||||
*/
|
||||
public WrappedWatchableObject(WatchableObject handle) {
|
||||
public WrappedWatchableObject(Object handle) {
|
||||
load(handle);
|
||||
}
|
||||
|
||||
@ -41,6 +49,7 @@ public class WrappedWatchableObject {
|
||||
* @param index - the index.
|
||||
* @param value - non-null value of specific types.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public WrappedWatchableObject(int index, Object value) {
|
||||
if (value == null)
|
||||
throw new IllegalArgumentException("Value cannot be NULL.");
|
||||
@ -49,24 +58,44 @@ public class WrappedWatchableObject {
|
||||
Integer typeID = WrappedDataWatcher.getTypeID(value.getClass());
|
||||
|
||||
if (typeID != null) {
|
||||
load(new WatchableObject(typeID, index, getUnwrapped(value)));
|
||||
if (watchableConstructor == null) {
|
||||
try {
|
||||
watchableConstructor = MinecraftReflection.getWatchableObjectClass().
|
||||
getConstructor(int.class, int.class, Object.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot get the WatchableObject(int, int, Object) constructor.", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the object
|
||||
try {
|
||||
load(watchableConstructor.newInstance(typeID, index, getUnwrapped(value)));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot construct underlying WatchableObject.", e);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot watch the type " + value.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap a NMS object
|
||||
private void load(WatchableObject handle) {
|
||||
private void load(Object handle) {
|
||||
initialize();
|
||||
this.handle = handle;
|
||||
this.modifier = baseModifier.withTarget(handle);
|
||||
|
||||
// Make sure the type is correct
|
||||
if (!watchableObjectClass.isAssignableFrom(handle.getClass())) {
|
||||
throw new ClassCastException("Cannot cast the class " + handle.getClass().getName() +
|
||||
" to " + watchableObjectClass.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the underlying watchable object.
|
||||
* @return The underlying watchable object.
|
||||
*/
|
||||
public WatchableObject getHandle() {
|
||||
public Object getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -76,7 +105,8 @@ public class WrappedWatchableObject {
|
||||
private static void initialize() {
|
||||
if (!hasInitialized) {
|
||||
hasInitialized = true;
|
||||
baseModifier = new StructureModifier<Object>(WatchableObject.class, null, false);
|
||||
watchableObjectClass = MinecraftReflection.getWatchableObjectClass();
|
||||
baseModifier = new StructureModifier<Object>(watchableObjectClass, null, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,12 +234,13 @@ public class WrappedWatchableObject {
|
||||
* @param value - the raw NMS object to wrap.
|
||||
* @return The wrapped object.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
static Object getWrapped(Object value) {
|
||||
// Handle the special cases
|
||||
if (value instanceof net.minecraft.server.ItemStack) {
|
||||
if (MinecraftReflection.isItemStack(value)) {
|
||||
return BukkitConverters.getItemStackConverter().getSpecific(value);
|
||||
} else if (value instanceof ChunkCoordinates) {
|
||||
return new WrappedChunkCoordinate((ChunkCoordinates) value);
|
||||
} else if (MinecraftReflection.isChunkCoordinates(value)) {
|
||||
return new WrappedChunkCoordinate((Comparable) value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
@ -221,9 +252,9 @@ public class WrappedWatchableObject {
|
||||
* @return The wrapped class type.
|
||||
*/
|
||||
static Class<?> getWrappedType(Class<?> unwrapped) {
|
||||
if (unwrapped.equals(net.minecraft.server.ChunkPosition.class))
|
||||
if (unwrapped.equals(MinecraftReflection.getChunkPositionClass()))
|
||||
return ChunkPosition.class;
|
||||
else if (unwrapped.equals(ChunkCoordinates.class))
|
||||
else if (unwrapped.equals(MinecraftReflection.getChunkCoordinatesClass()))
|
||||
return WrappedChunkCoordinate.class;
|
||||
else
|
||||
return unwrapped;
|
||||
@ -240,7 +271,7 @@ public class WrappedWatchableObject {
|
||||
return ((WrappedChunkCoordinate) wrapped).getHandle();
|
||||
else if (wrapped instanceof ItemStack)
|
||||
return BukkitConverters.getItemStackConverter().getGeneric(
|
||||
net.minecraft.server.ItemStack.class, (org.bukkit.inventory.ItemStack) wrapped);
|
||||
MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
|
||||
else
|
||||
return wrapped;
|
||||
}
|
||||
@ -252,9 +283,9 @@ public class WrappedWatchableObject {
|
||||
*/
|
||||
static Class<?> getUnwrappedType(Class<?> wrapped) {
|
||||
if (wrapped.equals(ChunkPosition.class))
|
||||
return net.minecraft.server.ChunkPosition.class;
|
||||
return MinecraftReflection.getChunkPositionClass();
|
||||
else if (wrapped.equals(WrappedChunkCoordinate.class))
|
||||
return ChunkCoordinates.class;
|
||||
return MinecraftReflection.getChunkCoordinatesClass();
|
||||
else
|
||||
return wrapped;
|
||||
}
|
||||
@ -265,7 +296,8 @@ public class WrappedWatchableObject {
|
||||
* @throws FieldAccessException If we're unable to use reflection.
|
||||
*/
|
||||
public WrappedWatchableObject deepClone() throws FieldAccessException {
|
||||
WrappedWatchableObject clone = new WrappedWatchableObject(DefaultInstances.DEFAULT.getDefault(WatchableObject.class));
|
||||
@SuppressWarnings("unchecked")
|
||||
WrappedWatchableObject clone = new WrappedWatchableObject(DefaultInstances.DEFAULT.getDefault(MinecraftReflection.getWatchableObjectClass()));
|
||||
|
||||
clone.setDirtyState(getDirtyState());
|
||||
clone.setIndex(getIndex());
|
||||
@ -275,18 +307,48 @@ public class WrappedWatchableObject {
|
||||
}
|
||||
|
||||
// Helper
|
||||
private Object getClonedValue() throws FieldAccessException {
|
||||
Object getClonedValue() throws FieldAccessException {
|
||||
Object value = getValue();
|
||||
|
||||
// Only a limited set of references types are supported
|
||||
if (value instanceof net.minecraft.server.ChunkPosition) {
|
||||
if (MinecraftReflection.isChunkPosition(value)) {
|
||||
EquivalentConverter<ChunkPosition> converter = ChunkPosition.getConverter();
|
||||
return converter.getGeneric(net.minecraft.server.ChunkPosition.class, converter.getSpecific(value));
|
||||
} else if (value instanceof ItemStack) {
|
||||
return ((ItemStack) value).cloneItemStack();
|
||||
return converter.getGeneric(MinecraftReflection.getChunkPositionClass(), converter.getSpecific(value));
|
||||
} else if (MinecraftReflection.isItemStack(value)) {
|
||||
return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(value).clone());
|
||||
} else {
|
||||
// A string or primitive wrapper, which are all immutable.
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// Quick checks
|
||||
if (obj == this)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
|
||||
if (obj instanceof WrappedWatchableObject) {
|
||||
WrappedWatchableObject other = (WrappedWatchableObject) obj;
|
||||
|
||||
return Objects.equal(getIndex(), other.getIndex()) &&
|
||||
Objects.equal(getTypeID(), other.getTypeID()) &&
|
||||
Objects.equal(getValue(), other.getValue());
|
||||
}
|
||||
|
||||
// No, this is not equivalent
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(getIndex(), getTypeID(), getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[%s: %s (%s)]", getIndex(), getValue(), getType().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
@ -12,4 +12,7 @@ global:
|
||||
metrics: true
|
||||
|
||||
# Automatically compile structure modifiers
|
||||
background compiler: true
|
||||
background compiler: true
|
||||
|
||||
# Disable version checking for the given Minecraft version. Backup your world first!
|
||||
ignore version check:
|
@ -1,9 +1,9 @@
|
||||
name: ProtocolLib
|
||||
version: 1.7.1
|
||||
version: 1.8.0
|
||||
description: Provides read/write access to the Minecraft protocol.
|
||||
author: Comphenix
|
||||
website: http://www.comphenix.net/ProtocolLib
|
||||
|
||||
load: startup
|
||||
main: com.comphenix.protocol.ProtocolLibrary
|
||||
database: false
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class MinecraftVersionTest {
|
||||
|
||||
@Test
|
||||
public void testComparision() {
|
||||
MinecraftVersion within = new MinecraftVersion(1, 2, 5);
|
||||
MinecraftVersion outside = new MinecraftVersion(1, 7, 0);
|
||||
|
||||
MinecraftVersion lower = new MinecraftVersion(1, 0, 0);
|
||||
MinecraftVersion highest = new MinecraftVersion(1, 4, 5);
|
||||
|
||||
// Make sure this is valid
|
||||
assertTrue(lower.compareTo(within) < 0 && within.compareTo(highest) < 0);
|
||||
assertFalse(outside.compareTo(within) < 0 && outside.compareTo(highest) < 0);
|
||||
}
|
||||
|
||||
public void testParsing() {
|
||||
assertEquals(MinecraftVersion.extractVersion("CraftBukkit R3.0 (MC: 1.4.3)"), "1.4.3");
|
||||
assertEquals(MinecraftVersion.extractVersion("CraftBukkit Test Beta 1 (MC: 1.10.01 )"), "1.10.01");
|
||||
assertEquals(MinecraftVersion.extractVersion("Hello (MC: 2.3.4 ) "), "2.3.4");
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user