Fix players being disconnected on login

This commit is contained in:
Dan Mulloy 2014-11-29 11:58:43 -05:00
parent b9818cace7
commit 670e8f034c
5 changed files with 196 additions and 273 deletions

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.comphenix.protocol</groupId> <groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId> <artifactId>ProtocolLib</artifactId>
<version>3.6.1-SNAPSHOT</version> <version>3.6.2-SNAPSHOT</version>
<name>ProtocolLib</name> <name>ProtocolLib</name>
<description>Provides read/write access to the Minecraft protocol.</description> <description>Provides read/write access to the Minecraft protocol.</description>

View File

@ -1,27 +0,0 @@
package com.comphenix.protocol.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicate that this API and its descendants are only valid on Spigot.
* @author Kristian
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PACKAGE,
ElementType.PARAMETER, ElementType.TYPE, ElementType.FIELD})
public @interface Spigot {
/**
* The minimum build number of Spigot where this is valid.
* @return The minimum build.
*/
int minimumBuild();
/**
* The maximum build number of Spigot where this is valid, or Integer.MAX_VALUE if not set.
* @return The maximum build number.
*/
int maximumBuild() default Integer.MAX_VALUE;
}

View File

@ -34,7 +34,6 @@ import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType; import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol; import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.annotations.Spigot;
import com.comphenix.protocol.error.Report; import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType; import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide; import com.comphenix.protocol.events.ConnectionSide;
@ -82,9 +81,6 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
// For retrieving the protocol // For retrieving the protocol
private static FieldAccessor PROTOCOL_ACCESSOR; private static FieldAccessor PROTOCOL_ACCESSOR;
// Current version
private static volatile MethodAccessor PROTOCOL_VERSION;
// The factory that created this injector // The factory that created this injector
private InjectionFactory factory; private InjectionFactory factory;
@ -171,27 +167,9 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
* Get the version of the current protocol. * Get the version of the current protocol.
* @return The version. * @return The version.
*/ */
@Spigot(minimumBuild = 1628)
@Override @Override
public int getProtocolVersion() { public int getProtocolVersion() {
MethodAccessor accessor = PROTOCOL_VERSION; return MinecraftProtocolVersion.getCurrentVersion();
if (accessor == null) {
try {
accessor = Accessors.getMethodAccessor(networkManager.getClass(), "getVersion");
} catch (RuntimeException e) {
// Notify user
ProtocolLibrary.getErrorReporter().reportWarning(
this, Report.newBuilder(REPORT_CANNOT_FIND_GET_VERSION).error(e));
// Fallback method
accessor = Accessors.getConstantAccessor(
MinecraftProtocolVersion.getCurrentVersion(), null);
}
PROTOCOL_VERSION = accessor;
}
return (Integer) accessor.invoke(networkManager);
} }
@Override @Override
@ -271,13 +249,13 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
// Intercept all write methods // Intercept all write methods
channelField.setValue(new ChannelProxy(originalChannel, MinecraftReflection.getPacketClass()) { channelField.setValue(new ChannelProxy(originalChannel, MinecraftReflection.getPacketClass()) {
// Compatibility with Spigot 1.8 protocol hack // Compatibility with Spigot 1.8
private final PipelineProxy pipelineProxy = new PipelineProxy(originalChannel.pipeline(), this) { private final PipelineProxy pipelineProxy = new PipelineProxy(originalChannel.pipeline(), this) {
@Override @Override
public ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler) { public ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler) {
// Correct the position of the decoder // Correct the position of the decoder
if ("decoder".equals(baseName)) { if ("decoder".equals(baseName)) {
if (super.get("protocol_lib_decoder") != null && guessSpigotHandler(handler)) { if (super.get("protocol_lib_decoder") != null && guessCompression(handler)) {
super.addBefore("protocol_lib_decoder", name, handler); super.addBefore("protocol_lib_decoder", name, handler);
return this; return this;
} }
@ -366,15 +344,13 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
} }
/** /**
* Determine if the given object is a Spigot channel handler. * Determine if the given object is a compressor or decompressor.
* @param handler - object to test. * @param handler - object to test.
* @return TRUE if it is, FALSE if not or unknown. * @return TRUE if it is, FALSE if not or unknown.
*/ */
private boolean guessSpigotHandler(ChannelHandler handler) { private boolean guessCompression(ChannelHandler handler) {
String className = handler != null ? handler.getClass().getCanonicalName() : null; String className = handler != null ? handler.getClass().getCanonicalName() : null;
return className.contains("PacketCompressor") || className.contains("PacketDecompressor");
return "org.spigotmc.SpigotDecompressor".equals(className) ||
"org.spigotmc.SpigotCompressor".equals(className);
} }
/** /**

View File

@ -37,22 +37,18 @@ import javax.annotation.Nullable;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.annotations.Spigot;
import com.comphenix.protocol.injector.BukkitUnwrapper; import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.ReadOnlyFieldAccessor; import com.comphenix.protocol.reflect.accessors.ReadOnlyFieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.collection.ConvertedMap; import com.comphenix.protocol.wrappers.collection.ConvertedMap;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterators; import com.google.common.collect.Iterators;
import com.google.common.collect.Maps;
/** /**
* Wraps a DataWatcher that is used to transmit arbitrary key-value pairs with a given entity. * Wraps a DataWatcher that is used to transmit arbitrary key-value pairs with a given entity.
@ -60,129 +56,127 @@ import com.google.common.collect.Maps;
* @author Kristian * @author Kristian
*/ */
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject> { public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject> {
/** // /**
* Every custom watchable type in Spigot #1628 and above. // * Every custom watchable type in Spigot #1628 and above.
* @author Kristian // * @author Kristian
*/ // */
@Spigot(minimumBuild = 1628) // public enum CustomType {
public enum CustomType { // BYTE_SHORT("org.spigotmc.ProtocolData$ByteShort", 0, short.class),
BYTE_SHORT("org.spigotmc.ProtocolData$ByteShort", 0, short.class), // DUAL_BYTE("org.spigotmc.ProtocolData$DualByte", 0, byte.class, byte.class),
DUAL_BYTE("org.spigotmc.ProtocolData$DualByte", 0, byte.class, byte.class), // HIDDEN_BYTE("org.spigotmc.ProtocolData$HiddenByte", 0, byte.class),
HIDDEN_BYTE("org.spigotmc.ProtocolData$HiddenByte", 0, byte.class), // INT_BYTE("org.spigotmc.ProtocolData$IntByte", 2, int.class, byte.class),
INT_BYTE("org.spigotmc.ProtocolData$IntByte", 2, int.class, byte.class), // DUAL_INT("org.spigotmc.ProtocolData$DualInt", 2, int.class, int.class);
DUAL_INT("org.spigotmc.ProtocolData$DualInt", 2, int.class, int.class); //
// private Class<?> spigotClass;
private Class<?> spigotClass; // private ConstructorAccessor constructor;
private ConstructorAccessor constructor; // private FieldAccessor secondaryValue;
private FieldAccessor secondaryValue; // private int typeId;
private int typeId; //
// private CustomType(String className, int typeId, Class<?>... parameters) {
private CustomType(String className, int typeId, Class<?>... parameters) { // try {
try { // this.spigotClass = Class.forName(className);
this.spigotClass = Class.forName(className); // this.constructor = Accessors.getConstructorAccessor(spigotClass, parameters);
this.constructor = Accessors.getConstructorAccessor(spigotClass, parameters); // this.secondaryValue = parameters.length > 1 ? Accessors.getFieldAccessor(spigotClass, "value2", true) : null;
this.secondaryValue = parameters.length > 1 ? Accessors.getFieldAccessor(spigotClass, "value2", true) : null; //
// } catch (ClassNotFoundException e) {
} catch (ClassNotFoundException e) { // System.out.println("[ProtocolLib] Unable to find " + className);
System.out.println("[ProtocolLib] Unable to find " + className); // this.spigotClass = null;
this.spigotClass = null; // }
} // this.typeId = typeId;
this.typeId = typeId; // }
} //
// /**
/** // * Construct a new instance of this Spigot type.
* Construct a new instance of this Spigot type. // * @param value - the value. Cannot be NULL.
* @param value - the value. Cannot be NULL. // * @return The instance to construct.
* @return The instance to construct. // */
*/ // Object newInstance(Object value) {
Object newInstance(Object value) { // return newInstance(value, null);
return newInstance(value, null); // }
} //
// /**
/** // * Construct a new instance of this Spigot type.
* Construct a new instance of this Spigot type. // * <p>
* <p> // * The secondary value may be NULL if this custom type does not contain a secondary value.
* The secondary value may be NULL if this custom type does not contain a secondary value. // * @param value - the value.
* @param value - the value. // * @param secondary - optional secondary value.
* @param secondary - optional secondary value. // * @return
* @return // */
*/ // Object newInstance(Object value, Object secondary) {
Object newInstance(Object value, Object secondary) { // Preconditions.checkNotNull(value, "value cannot be NULL.");
Preconditions.checkNotNull(value, "value cannot be NULL."); //
// if (hasSecondary()) {
if (hasSecondary()) { // return constructor.invoke(value, secondary);
return constructor.invoke(value, secondary); // } else {
} else { // if (secondary != null) {
if (secondary != null) { // throw new IllegalArgumentException("Cannot construct " + this + " with a secondary value");
throw new IllegalArgumentException("Cannot construct " + this + " with a secondary value"); // }
} // return constructor.invoke(value);
return constructor.invoke(value); // }
} // }
} //
// /**
/** // * Set the secondary value of a given type.
* Set the secondary value of a given type. // * @param instance - the instance.
* @param instance - the instance. // * @param secondary - the secondary value.
* @param secondary - the secondary value. // */
*/ // void setSecondary(Object instance, Object secondary) {
void setSecondary(Object instance, Object secondary) { // if (!hasSecondary()) {
if (!hasSecondary()) { // throw new IllegalArgumentException(this + " does not have a secondary value.");
throw new IllegalArgumentException(this + " does not have a secondary value."); // }
} // secondaryValue.set(instance, secondary);
secondaryValue.set(instance, secondary); // }
} //
// /**
/** // * Get the secondary value of a type.
* Get the secondary value of a type. // * @param instance - the instance.
* @param instance - the instance. // * @return The secondary value.
* @return The secondary value. // */
*/ // Object getSecondary(Object instance) {
Object getSecondary(Object instance) { // if (!hasSecondary()) {
if (!hasSecondary()) { // throw new IllegalArgumentException(this + " does not have a secondary value.");
throw new IllegalArgumentException(this + " does not have a secondary value."); // }
} // return secondaryValue.get(instance);
return secondaryValue.get(instance); // }
} //
// /**
/** // * Determine if this type has a secondary value.
* Determine if this type has a secondary value. // * @return TRUE if it does, FALSE otherwise.
* @return TRUE if it does, FALSE otherwise. // */
*/ // public boolean hasSecondary() {
public boolean hasSecondary() { // return secondaryValue != null;
return secondaryValue != null; // }
} //
// /**
/** // * Underlying Spigot class.
* Underlying Spigot class. // * @return The class.
* @return The class. // */
*/ // public Class<?> getSpigotClass() {
public Class<?> getSpigotClass() { // return spigotClass;
return spigotClass; // }
} //
// /**
/** // * The equivalent type ID.
* The equivalent type ID. // * @return The equivalent ID.
* @return The equivalent ID. // */
*/ // public int getTypeId() {
public int getTypeId() { // return typeId;
return typeId; // }
} //
// /**
/** // * Retrieve the custom Spigot type of a value.
* Retrieve the custom Spigot type of a value. // * @param value - the value.
* @param value - the value. // * @return The Spigot type, or NULL if not found.
* @return The Spigot type, or NULL if not found. // */
*/ // public static CustomType fromValue(Object value) {
@Spigot(minimumBuild = 1628) // for (CustomType type : CustomType.values()) {
public static CustomType fromValue(Object value) { // if (type.getSpigotClass().isInstance(value)) {
for (CustomType type : CustomType.values()) { // return type;
if (type.getSpigotClass().isInstance(value)) { // }
return type; // }
} // return null;
} // }
return null; // }
}
}
/** /**
* Used to assign integer IDs to given types. * Used to assign integer IDs to given types.
@ -434,23 +428,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @throws FieldAccessException Cannot read underlying field. * @throws FieldAccessException Cannot read underlying field.
*/ */
public Object getObject(int index) throws FieldAccessException { public Object getObject(int index) throws FieldAccessException {
// The get method will take care of concurrency
WrappedWatchableObject object = getWrappedObject(index);
return object != null ? object.getValue() : null;
}
/**
* Retrieve the wrapped object by index.
* @param index - the index.
* @return The corresponding wrapper, or NULL.
*/
@Spigot(minimumBuild = 1628)
public WrappedWatchableObject getWrappedObject(int index) {
// The get method will take care of concurrency
Object watchable = getWatchedObject(index); Object watchable = getWatchedObject(index);
if (watchable != null) { if (watchable != null) {
return new WrappedWatchableObject(watchable); return new WrappedWatchableObject(watchable).getValue();
} else { } else {
return null; return null;
} }
@ -620,21 +601,20 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
} }
} }
/** // /**
* Set a watched byte with an optional secondary value. // * Set a watched byte with an optional secondary value.
* @param index - index of the watched byte. // * @param index - index of the watched byte.
* @param newValue - the new watched value. // * @param newValue - the new watched value.
* @param secondary - optional secondary value. // * @param secondary - optional secondary value.
* @param update - whether or not to refresh every listening client. // * @param update - whether or not to refresh every listening client.
* @throws FieldAccessException Cannot read underlying field. // * @throws FieldAccessException Cannot read underlying field.
*/ // */
@Spigot(minimumBuild = 1628) // public void setObject(int index, Object newValue, Object secondary, boolean update, CustomType type) throws FieldAccessException {
public void setObject(int index, Object newValue, Object secondary, boolean update, CustomType type) throws FieldAccessException { // Object created = type.newInstance(newValue, secondary);
Object created = type.newInstance(newValue, secondary); //
// // Now update the watcher
// Now update the watcher // setObject(index, created, update);
setObject(index, created, update); // }
}
private Object getWatchedObject(int index) throws FieldAccessException { private Object getWatchedObject(int index) throws FieldAccessException {
// We use the get-method first and foremost // We use the get-method first and foremost
@ -738,7 +718,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
initializeSpigot(fuzzy); initializeSpigot(fuzzy);
// Any custom types // Any custom types
CUSTOM_MAP = initializeCustom(); // CUSTOM_MAP = initializeCustom();
// Initialize static type type // Initialize static type type
TYPE_MAP = (Map<Class<?>, Integer>) TYPE_MAP_ACCESSOR.get(null); TYPE_MAP = (Map<Class<?>, Integer>) TYPE_MAP_ACCESSOR.get(null);
@ -757,17 +737,17 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
initializeMethods(fuzzy); initializeMethods(fuzzy);
} }
// For Spigot's bountiful update patch // // For Spigot's bountiful update patch
private static Map<Class<?>, Integer> initializeCustom() { // private static Map<Class<?>, Integer> initializeCustom() {
Map<Class<?>, Integer> map = Maps.newHashMap(); // Map<Class<?>, Integer> map = Maps.newHashMap();
//
for (CustomType type : CustomType.values()) { // for (CustomType type : CustomType.values()) {
if (type.getSpigotClass() != null) { // if (type.getSpigotClass() != null) {
map.put(type.getSpigotClass(), type.getTypeId()); // map.put(type.getSpigotClass(), type.getTypeId());
} // }
} // }
return map; // return map;
} // }
// TODO: Remove, as this was fixed in build #1189 of Spigot // TODO: Remove, as this was fixed in build #1189 of Spigot
private static void initializeSpigot(FuzzyReflection fuzzy) { private static void initializeSpigot(FuzzyReflection fuzzy) {

View File

@ -21,13 +21,11 @@ import java.lang.reflect.Constructor;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.annotations.Spigot;
import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.CustomType;
import com.google.common.base.Objects; import com.google.common.base.Objects;
/** /**
@ -97,17 +95,16 @@ public class WrappedWatchableObject extends AbstractWrapper {
} }
} }
/** // /**
* Construct a custom watchable object from an index, value and custom type. // * Construct a custom watchable object from an index, value and custom type.
* @param index - the index. // * @param index - the index.
* @param primary - non-null value of specific types. // * @param primary - non-null value of specific types.
* @param secondary - optional secondary value, if the type can store it. // * @param secondary - optional secondary value, if the type can store it.
* @param type - the custom Spigot type. // * @param type - the custom Spigot type.
*/ // */
@Spigot(minimumBuild = 1628) // public WrappedWatchableObject(int index, Object value, Object secondary, CustomType type) {
public WrappedWatchableObject(int index, Object value, Object secondary, CustomType type) { // this(index, type.newInstance(value, secondary));
this(index, type.newInstance(value, secondary)); // }
}
// Wrap a NMS object // Wrap a NMS object
private void load(Object handle) { private void load(Object handle) {
@ -133,14 +130,13 @@ public class WrappedWatchableObject extends AbstractWrapper {
} }
} }
/** // /**
* Retrieve the custom type of this object. // * Retrieve the custom type of this object.
* @return The custom type, or NULL if not applicable. // * @return The custom type, or NULL if not applicable.
*/ // */
@Spigot(minimumBuild = 1628) // public CustomType getCustomType() {
public CustomType getCustomType() { // return CustomType.fromValue(getValue());
return CustomType.fromValue(getValue()); // }
}
/** /**
* Retrieve the correct super type of the current value. * Retrieve the correct super type of the current value.
@ -289,32 +285,30 @@ public class WrappedWatchableObject extends AbstractWrapper {
return getWrapped(modifier.withType(Object.class).read(0)); return getWrapped(modifier.withType(Object.class).read(0));
} }
/** // /**
* Retrieve the secondary value associated with this watchable object. // * Retrieve the secondary value associated with this watchable object.
* <p> // * <p>
* This is only applicable for certain {@link CustomType}. // * This is only applicable for certain {@link CustomType}.
* @return The secondary value, or NULL if not found. // * @return The secondary value, or NULL if not found.
*/ // */
@Spigot(minimumBuild = 1628) // public Object getSecondaryValue() {
public Object getSecondaryValue() { // CustomType type = getCustomType();
CustomType type = getCustomType(); // return type != null ? type.getSecondary(getValue()) : null;
return type != null ? type.getSecondary(getValue()) : null; // }
} //
// /**
/** // * Set the secondary value.
* Set the secondary value. // * @param secondary - the secondary value.
* @param secondary - the secondary value. // * @throws IllegalStateException If this watchable object does not have a secondary value.
* @throws IllegalStateException If this watchable object does not have a secondary value. // */
*/ // public void setSecondaryValue(Object secondary) {
@Spigot(minimumBuild = 1628) // CustomType type = getCustomType();
public void setSecondaryValue(Object secondary) { //
CustomType type = getCustomType(); // if (type == null) {
// throw new IllegalStateException(this + " does not have a custom type.");
if (type == null) { // }
throw new IllegalStateException(this + " does not have a custom type."); // type.setSecondary(getValue(), secondary);
} // }
type.setSecondary(getValue(), secondary);
}
/** /**
* Set whether or not the value must be synchronized with the client. * Set whether or not the value must be synchronized with the client.