fields = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldListByType(fieldClass);
FieldAccessor[] accessors = new FieldAccessor[fields.size()];
@@ -83,7 +83,7 @@ public final class Accessors {
* @return The value of that field.
* @throws IllegalArgumentException If the field cannot be found.
*/
- public static FieldAccessor getFieldAccessor(Class> instanceClass, String fieldName, boolean forceAccess) {
+ public static FieldAccessor getFieldAccessor(Class> instanceClass, String fieldName, boolean forceAccess) {
return Accessors.getFieldAccessor(ExactReflection.fromClass(instanceClass, true).getField(fieldName));
}
@@ -120,13 +120,27 @@ public final class Accessors {
// Verify the type
if (fieldType.isAssignableFrom(accessor.getField().getType())) {
- return accessor;
+ return accessor;
}
return null;
} catch (IllegalArgumentException e) {
return null;
}
}
+
+ /**
+ * Retrieve a method accessor for a field with the given name and equivalent type, or NULL.
+ * @param clazz - the declaration class.
+ * @param methodName - the method name.
+ * @return The method accessor, or NULL if not found.
+ */
+ public static MethodAccessor getMethodAcccessorOrNull(Class> clazz, String methodName) {
+ try {
+ return Accessors.getMethodAccessor(clazz, methodName);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
/**
* Find a specific constructor in a class.
@@ -145,7 +159,7 @@ public final class Accessors {
/**
* Retrieve a field accessor that will cache the content of the field.
*
- * Note that we don't check if the underlying field has changed after the value has been cached,
+ * Note that we don't check if the underlying field has changed after the value has been cached,
* so it's best to use this on final fields.
* @param inner - the accessor.
* @return A cached field accessor.
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/cloning/BukkitCloner.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/cloning/BukkitCloner.java
index dedaf7f2..3098a60d 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/cloning/BukkitCloner.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/cloning/BukkitCloner.java
@@ -40,12 +40,21 @@ public class BukkitCloner implements Cloner {
List> classes = Lists.newArrayList();
classes.add(MinecraftReflection.getItemStackClass());
- classes.add(MinecraftReflection.getBlockPositionClass());
classes.add(MinecraftReflection.getDataWatcherClass());
+ // Try to add position classes
+ try {
+ classes.add(MinecraftReflection.getBlockPositionClass());
+ } catch (Throwable ex) { }
+
+ try {
+ classes.add(MinecraftReflection.getChunkPositionClass());
+ } catch (Throwable ex) { }
+
if (MinecraftReflection.isUsingNetty()) {
classes.add(MinecraftReflection.getServerPingClass());
}
+
this.clonableClasses = classes.toArray(new Class>[0]);
}
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/instances/DefaultInstances.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/instances/DefaultInstances.java
index 3e13de80..ee14eef3 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/instances/DefaultInstances.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/instances/DefaultInstances.java
@@ -290,7 +290,6 @@ public class DefaultInstances implements InstanceProvider {
return createInstance(type, minimum, types, params);
}
-
} catch (Exception e) {
// Nope, we couldn't create this type. Might for instance be NotConstructableException.
}
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java
index f599f543..6c7bcb01 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java
@@ -596,8 +596,17 @@ public class MinecraftReflection {
if (!isUsingNetty())
throw new IllegalStateException("GameProfile does not exist in version 1.6.4 and earlier.");
- // Yay, we can actually refer to it directly
- return GameProfile.class;
+ try {
+ return GameProfile.class;
+ } catch (Throwable ex) {
+ // As far as I can tell, the named entity spawn packet is the only packet that uses GameProfiles
+ FuzzyReflection reflection = FuzzyReflection.fromClass(PacketType.Play.Server.NAMED_ENTITY_SPAWN.getPacketClass(), true);
+ FuzzyFieldContract contract = FuzzyFieldContract.newBuilder()
+ .banModifier(Modifier.STATIC)
+ .typeMatches(FuzzyMatchers.matchRegex("(.*)(GameProfile)", 1))
+ .build();
+ return reflection.getField(contract).getType();
+ }
}
/**
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedBlockData.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedBlockData.java
index d72f05d7..768642cd 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedBlockData.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedBlockData.java
@@ -1,5 +1,18 @@
/**
- * (c) 2015 dmulloy2
+ * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
+ * Copyright (C) 2015 dmulloy2
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program;
+ * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java
index 43e2c169..4c61e71f 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java
@@ -31,24 +31,29 @@ import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Level;
import javax.annotation.Nullable;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
+import com.comphenix.protocol.ProtocolLibrary;
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.reflect.accessors.Accessors;
+import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.ReadOnlyFieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
import com.google.common.base.Function;
import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
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.
@@ -56,127 +61,127 @@ import com.google.common.collect.Iterators;
* @author Kristian
*/
public class WrappedDataWatcher extends AbstractWrapper implements Iterable {
-// /**
-// * Every custom watchable type in Spigot #1628 and above.
-// * @author Kristian
-// */
-// public enum CustomType {
-// BYTE_SHORT("org.spigotmc.ProtocolData$ByteShort", 0, short.class),
-// DUAL_BYTE("org.spigotmc.ProtocolData$DualByte", 0, byte.class, byte.class),
-// HIDDEN_BYTE("org.spigotmc.ProtocolData$HiddenByte", 0, byte.class),
-// INT_BYTE("org.spigotmc.ProtocolData$IntByte", 2, int.class, byte.class),
-// DUAL_INT("org.spigotmc.ProtocolData$DualInt", 2, int.class, int.class);
-//
-// private Class> spigotClass;
-// private ConstructorAccessor constructor;
-// private FieldAccessor secondaryValue;
-// private int typeId;
-//
-// private CustomType(String className, int typeId, Class>... parameters) {
-// try {
-// this.spigotClass = Class.forName(className);
-// this.constructor = Accessors.getConstructorAccessor(spigotClass, parameters);
-// this.secondaryValue = parameters.length > 1 ? Accessors.getFieldAccessor(spigotClass, "value2", true) : null;
-//
-// } catch (ClassNotFoundException e) {
-// ProtocolLibrary.log(Level.WARNING, "Unable to find " + className);
-// this.spigotClass = null;
-// }
-// this.typeId = typeId;
-// }
-//
-// /**
-// * Construct a new instance of this Spigot type.
-// * @param value - the value. Cannot be NULL.
-// * @return The instance to construct.
-// */
-// Object newInstance(Object value) {
-// return newInstance(value, null);
-// }
-//
-// /**
-// * Construct a new instance of this Spigot type.
-// *
-// * The secondary value may be NULL if this custom type does not contain a secondary value.
-// * @param value - the value.
-// * @param secondary - optional secondary value.
-// * @return
-// */
-// Object newInstance(Object value, Object secondary) {
-// Preconditions.checkNotNull(value, "value cannot be NULL.");
-//
-// if (hasSecondary()) {
-// return constructor.invoke(value, secondary);
-// } else {
-// if (secondary != null) {
-// throw new IllegalArgumentException("Cannot construct " + this + " with a secondary value");
-// }
-// return constructor.invoke(value);
-// }
-// }
-//
-// /**
-// * Set the secondary value of a given type.
-// * @param instance - the instance.
-// * @param secondary - the secondary value.
-// */
-// void setSecondary(Object instance, Object secondary) {
-// if (!hasSecondary()) {
-// throw new IllegalArgumentException(this + " does not have a secondary value.");
-// }
-// secondaryValue.set(instance, secondary);
-// }
-//
-// /**
-// * Get the secondary value of a type.
-// * @param instance - the instance.
-// * @return The secondary value.
-// */
-// Object getSecondary(Object instance) {
-// if (!hasSecondary()) {
-// throw new IllegalArgumentException(this + " does not have a secondary value.");
-// }
-// return secondaryValue.get(instance);
-// }
-//
-// /**
-// * Determine if this type has a secondary value.
-// * @return TRUE if it does, FALSE otherwise.
-// */
-// public boolean hasSecondary() {
-// return secondaryValue != null;
-// }
-//
-// /**
-// * Underlying Spigot class.
-// * @return The class.
-// */
-// public Class> getSpigotClass() {
-// return spigotClass;
-// }
-//
-// /**
-// * The equivalent type ID.
-// * @return The equivalent ID.
-// */
-// public int getTypeId() {
-// return typeId;
-// }
-//
-// /**
-// * Retrieve the custom Spigot type of a value.
-// * @param value - the value.
-// * @return The Spigot type, or NULL if not found.
-// */
-// public static CustomType fromValue(Object value) {
-// for (CustomType type : CustomType.values()) {
-// if (type.getSpigotClass().isInstance(value)) {
-// return type;
-// }
-// }
-// return null;
-// }
-// }
+ /**
+ * Every custom watchable type in Spigot #1628 and above.
+ * @author Kristian
+ */
+ public enum CustomType {
+ BYTE_SHORT("org.spigotmc.ProtocolData$ByteShort", 0, short.class),
+ DUAL_BYTE("org.spigotmc.ProtocolData$DualByte", 0, byte.class, byte.class),
+ HIDDEN_BYTE("org.spigotmc.ProtocolData$HiddenByte", 0, byte.class),
+ INT_BYTE("org.spigotmc.ProtocolData$IntByte", 2, int.class, byte.class),
+ DUAL_INT("org.spigotmc.ProtocolData$DualInt", 2, int.class, int.class);
+
+ private Class> spigotClass;
+ private ConstructorAccessor constructor;
+ private FieldAccessor secondaryValue;
+ private int typeId;
+
+ private CustomType(String className, int typeId, Class>... parameters) {
+ try {
+ this.spigotClass = Class.forName(className);
+ this.constructor = Accessors.getConstructorAccessor(spigotClass, parameters);
+ this.secondaryValue = parameters.length > 1 ? Accessors.getFieldAccessor(spigotClass, "value2", true) : null;
+
+ } catch (ClassNotFoundException e) {
+ ProtocolLibrary.log(Level.WARNING, "Unable to find " + className);
+ this.spigotClass = null;
+ }
+ this.typeId = typeId;
+ }
+
+ /**
+ * Construct a new instance of this Spigot type.
+ * @param value - the value. Cannot be NULL.
+ * @return The instance to construct.
+ */
+ Object newInstance(Object value) {
+ return newInstance(value, null);
+ }
+
+ /**
+ * Construct a new instance of this Spigot type.
+ *
+ * The secondary value may be NULL if this custom type does not contain a secondary value.
+ * @param value - the value.
+ * @param secondary - optional secondary value.
+ * @return
+ */
+ Object newInstance(Object value, Object secondary) {
+ Preconditions.checkNotNull(value, "value cannot be NULL.");
+
+ if (hasSecondary()) {
+ return constructor.invoke(value, secondary);
+ } else {
+ if (secondary != null) {
+ throw new IllegalArgumentException("Cannot construct " + this + " with a secondary value");
+ }
+ return constructor.invoke(value);
+ }
+ }
+
+ /**
+ * Set the secondary value of a given type.
+ * @param instance - the instance.
+ * @param secondary - the secondary value.
+ */
+ void setSecondary(Object instance, Object secondary) {
+ if (!hasSecondary()) {
+ throw new IllegalArgumentException(this + " does not have a secondary value.");
+ }
+ secondaryValue.set(instance, secondary);
+ }
+
+ /**
+ * Get the secondary value of a type.
+ * @param instance - the instance.
+ * @return The secondary value.
+ */
+ Object getSecondary(Object instance) {
+ if (!hasSecondary()) {
+ throw new IllegalArgumentException(this + " does not have a secondary value.");
+ }
+ return secondaryValue.get(instance);
+ }
+
+ /**
+ * Determine if this type has a secondary value.
+ * @return TRUE if it does, FALSE otherwise.
+ */
+ public boolean hasSecondary() {
+ return secondaryValue != null;
+ }
+
+ /**
+ * Underlying Spigot class.
+ * @return The class.
+ */
+ public Class> getSpigotClass() {
+ return spigotClass;
+ }
+
+ /**
+ * The equivalent type ID.
+ * @return The equivalent ID.
+ */
+ public int getTypeId() {
+ return typeId;
+ }
+
+ /**
+ * Retrieve the custom Spigot type of a value.
+ * @param value - the value.
+ * @return The Spigot type, or NULL if not found.
+ */
+ public static CustomType fromValue(Object value) {
+ for (CustomType type : CustomType.values()) {
+ if (type.getSpigotClass().isInstance(value)) {
+ return type;
+ }
+ }
+ return null;
+ }
+ }
/**
* Used to assign integer IDs to given types.
@@ -601,20 +606,20 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable, Integer>) TYPE_MAP_ACCESSOR.get(null);
@@ -737,17 +742,17 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable, Integer> initializeCustom() {
-// Map, Integer> map = Maps.newHashMap();
-//
-// for (CustomType type : CustomType.values()) {
-// if (type.getSpigotClass() != null) {
-// map.put(type.getSpigotClass(), type.getTypeId());
-// }
-// }
-// return map;
-// }
+ // For Spigot's bountiful update patch
+ private static Map, Integer> initializeCustom() {
+ Map, Integer> map = Maps.newHashMap();
+
+ for (CustomType type : CustomType.values()) {
+ if (type.getSpigotClass() != null) {
+ map.put(type.getSpigotClass(), type.getTypeId());
+ }
+ }
+ return map;
+ }
// TODO: Remove, as this was fixed in build #1189 of Spigot
private static void initializeSpigot(FuzzyReflection fuzzy) {
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedGameProfile.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedGameProfile.java
index 271ea372..b4bc044d 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedGameProfile.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedGameProfile.java
@@ -14,13 +14,12 @@ import com.comphenix.protocol.injector.BukkitUnwrapper;
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.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.collection.ConvertedMultimap;
import com.google.common.base.Charsets;
import com.google.common.base.Objects;
import com.google.common.collect.Multimap;
-import com.mojang.authlib.GameProfile;
-import com.mojang.authlib.properties.Property;
/**
* Represents a wrapper for a game profile.
@@ -28,62 +27,79 @@ import com.mojang.authlib.properties.Property;
*/
public class WrappedGameProfile extends AbstractWrapper {
public static final ReportType REPORT_INVALID_UUID = new ReportType("Plugin %s created a profile with '%s' as an UUID.");
-
- // Version 1.7.2 and 1.7.8 respectively
- private static final ConstructorAccessor CREATE_STRING_STRING = Accessors.getConstructorAccessorOrNull(GameProfile.class, String.class, String.class);
- private static final FieldAccessor GET_UUID_STRING = Accessors.getFieldAcccessorOrNull(GameProfile.class, "id", String.class);
-
+
+ private static final Class> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
+
+ private static final ConstructorAccessor CREATE_STRING_STRING = Accessors.getConstructorAccessorOrNull(
+ GAME_PROFILE, String.class, String.class);
+ private static final ConstructorAccessor CREATE_UUID_STRING = Accessors.getConstructorAccessorOrNull(
+ GAME_PROFILE, UUID.class, String.class);
+
+ private static final FieldAccessor GET_UUID_STRING = Accessors.getFieldAcccessorOrNull(
+ GAME_PROFILE, "id", String.class);
+
+ private static final MethodAccessor GET_ID = Accessors.getMethodAcccessorOrNull(
+ GAME_PROFILE, "getId");
+ private static final MethodAccessor GET_NAME = Accessors.getMethodAcccessorOrNull(
+ GAME_PROFILE, "getName");
+ private static final MethodAccessor GET_PROPERTIES = Accessors.getMethodAcccessorOrNull(
+ GAME_PROFILE, "getProperties");
+ private static final MethodAccessor IS_COMPLETE = Accessors.getMethodAcccessorOrNull(
+ GAME_PROFILE, "isComplete");
+
// Fetching game profile
private static FieldAccessor PLAYER_PROFILE;
private static FieldAccessor OFFLINE_PROFILE;
-
+
// Property map
private Multimap propertyMap;
-
+
// Parsed UUID
private volatile UUID parsedUUID;
-
+
// Profile from a handle
private WrappedGameProfile(Object profile) {
- super(GameProfile.class);
+ super(GAME_PROFILE);
setHandle(profile);
}
-
+
/**
* Retrieve the associated game profile of a player.
*
* Note that this may not exist in the current Minecraft version.
+ *
* @param player - the player.
* @return The game profile.
*/
public static WrappedGameProfile fromPlayer(Player player) {
FieldAccessor accessor = PLAYER_PROFILE;
- Object nmsPlayer = BukkitUnwrapper.getInstance().unwrapItem(player);
-
if (accessor == null) {
- accessor = Accessors.getFieldAccessor(MinecraftReflection.getEntityHumanClass(), GameProfile.class, true);
+ accessor = Accessors.getFieldAccessor(MinecraftReflection.getEntityHumanClass(), GAME_PROFILE, true);
PLAYER_PROFILE = accessor;
}
+
+ Object nmsPlayer = BukkitUnwrapper.getInstance().unwrapItem(player);
return WrappedGameProfile.fromHandle(PLAYER_PROFILE.get(nmsPlayer));
}
-
+
/**
* Retrieve the associated game profile of an offline player.
*
* Note that this may not exist in the current Minecraft version.
+ *
* @param player - the offline player.
* @return The game profile.
*/
public static WrappedGameProfile fromOfflinePlayer(OfflinePlayer player) {
FieldAccessor accessor = OFFLINE_PROFILE;
-
if (accessor == null) {
- accessor = Accessors.getFieldAccessor(player.getClass(), GameProfile.class, true);
+ accessor = Accessors.getFieldAccessor(player.getClass(), GAME_PROFILE, true);
OFFLINE_PROFILE = accessor;
}
+
return WrappedGameProfile.fromHandle(OFFLINE_PROFILE.get(player));
}
-
+
/**
* Construct a new game profile with the given properties.
*
@@ -91,49 +107,58 @@ public class WrappedGameProfile extends AbstractWrapper {
* IDs that cannot be parsed as an UUID will be hashed and form a version 3 UUID instead.
*
* This method is deprecated for Minecraft 1.7.8 and above.
+ *
* @param id - the UUID of the player.
* @param name - the name of the player.
*/
@Deprecated
public WrappedGameProfile(String id, String name) {
- super(GameProfile.class);
-
+ super(GAME_PROFILE);
+
if (CREATE_STRING_STRING != null) {
setHandle(CREATE_STRING_STRING.invoke(id, name));
+ } else if (CREATE_UUID_STRING != null) {
+ setHandle(CREATE_UUID_STRING.invoke(parseUUID(id), name));
} else {
- setHandle(new GameProfile(parseUUID(id), name));
+ throw new IllegalArgumentException("Unsupported GameProfile constructor.");
}
}
-
+
/**
* Construct a new game profile with the given properties.
*
* Note that at least one of the parameters must be non-null.
+ *
* @param uuid - the UUID of the player, or NULL.
* @param name - the name of the player, or NULL.
*/
public WrappedGameProfile(UUID uuid, String name) {
- super(GameProfile.class);
-
+ super(GAME_PROFILE);
+
if (CREATE_STRING_STRING != null) {
setHandle(CREATE_STRING_STRING.invoke(uuid != null ? uuid.toString() : null, name));
+ } else if (CREATE_UUID_STRING != null) {
+ setHandle(CREATE_UUID_STRING.invoke(uuid, name));
} else {
- setHandle(new GameProfile(uuid, name));
+ throw new IllegalArgumentException("Unsupported GameProfile constructor.");
}
}
-
+
/**
* Construct a wrapper around an existing game profile.
+ *
* @param profile - the underlying profile, or NULL.
*/
public static WrappedGameProfile fromHandle(Object handle) {
if (handle == null)
return null;
+
return new WrappedGameProfile(handle);
}
-
+
/**
* Parse an UUID using very lax rules, as specified in {@link #WrappedGameProfile(UUID, String)}.
+ *
* @param id - text.
* @return The corresponding UUID.
* @throws IllegalArgumentException If we cannot parse the text.
@@ -143,13 +168,10 @@ public class WrappedGameProfile extends AbstractWrapper {
return id != null ? UUID.fromString(id) : null;
} catch (IllegalArgumentException e) {
// Warn once every hour (per plugin)
- ProtocolLibrary.getErrorReporter().reportWarning(
- WrappedGameProfile.class,
- Report.newBuilder(REPORT_INVALID_UUID).
- rateLimit(1, TimeUnit.HOURS).
- messageParam(PluginContext.getPluginCaller(new Exception()), id)
- );
-
+ ProtocolLibrary.getErrorReporter()
+ .reportWarning(WrappedGameProfile.class, Report.newBuilder(REPORT_INVALID_UUID)
+ .rateLimit(1, TimeUnit.HOURS)
+ .messageParam(PluginContext.getPluginCaller(new Exception()), id));
return UUID.nameUUIDFromBytes(id.getBytes(Charsets.UTF_8));
}
}
@@ -157,70 +179,86 @@ public class WrappedGameProfile extends AbstractWrapper {
/**
* Retrieve the UUID of the player.
*
- * Note that Minecraft 1.7.5 and earlier doesn't use UUIDs internally, and it may not be possible
- * to convert the string to an UUID.
+ * Note that Minecraft 1.7.5 and earlier doesn't use UUIDs internally, and it may not be possible to convert the string to an UUID.
*
* We use the same lax conversion as in {@link #WrappedGameProfile(String, String)}.
+ *
* @return The UUID, or NULL if the UUID is NULL.
* @throws IllegalStateException If we cannot parse the internal ID as an UUID.
*/
public UUID getUUID() {
UUID uuid = parsedUUID;
-
+
if (uuid == null) {
try {
if (GET_UUID_STRING != null) {
uuid = parseUUID(getId());
+ } else if (GET_ID != null) {
+ uuid = (UUID) GET_ID.invoke(handle);
} else {
- uuid = getProfile().getId();
+ throw new IllegalStateException("Unsupported getId() method");
}
+
// Cache for later
parsedUUID = uuid;
} catch (IllegalArgumentException e) {
- throw new IllegalStateException("Cannot parse ID " + getId() + " as an UUID in player profile " + getName());
+ throw new IllegalStateException("Cannot parse ID " + getId() + " as an UUID in player profile " + getName(), e);
}
}
+
return uuid;
}
-
+
/**
* Retrieve the textual representation of the player's UUID.
*
* Note that there's nothing stopping plugins from creating non-standard UUIDs.
*
* In Minecraft 1.7.8 and later, this simply returns the string form of {@link #getUUID()}.
+ *
* @return The UUID of the player, or NULL if not computed.
*/
public String getId() {
- if (GET_UUID_STRING != null)
+ if (GET_UUID_STRING != null) {
return (String) GET_UUID_STRING.get(handle);
- final GameProfile profile = getProfile();
- return profile.getId() != null ? profile.getId().toString() : null;
+ } else if (GET_ID != null) {
+ UUID uuid = (UUID) GET_ID.invoke(handle);
+ return uuid != null ? uuid.toString() : null;
+ } else {
+ throw new IllegalStateException("Unsupported getId() method");
+ }
}
/**
* Retrieve the name of the player.
+ *
* @return The player name.
*/
public String getName() {
- return getProfile().getName();
+ if (GET_NAME != null) {
+ return (String) GET_NAME.invoke(handle);
+ } else {
+ throw new IllegalStateException("Unsupported getName() method");
+ }
}
-
+
/**
* Retrieve the property map of signed values.
+ *
* @return Property map.
*/
+ @SuppressWarnings({ "unchecked", "rawtypes" })
public Multimap getProperties() {
Multimap result = propertyMap;
if (result == null) {
- result = new ConvertedMultimap(
- GuavaWrappers.getBukkitMultimap(getProfile().getProperties())) {
+ Multimap properties = (Multimap) GET_PROPERTIES.invoke(handle);
+ result = new ConvertedMultimap(GuavaWrappers.getBukkitMultimap(properties)) {
@Override
- protected Property toInner(WrappedSignedProperty outer) {
- return (Property) outer.handle;
+ protected Object toInner(WrappedSignedProperty outer) {
+ return outer.handle;
}
-
+
@Override
protected Object toInnerObject(Object outer) {
if (outer instanceof WrappedSignedProperty) {
@@ -228,9 +266,9 @@ public class WrappedGameProfile extends AbstractWrapper {
}
return outer;
}
-
+
@Override
- protected WrappedSignedProperty toOuter(Property inner) {
+ protected WrappedSignedProperty toOuter(Object inner) {
return WrappedSignedProperty.fromHandle(inner);
}
};
@@ -238,61 +276,57 @@ public class WrappedGameProfile extends AbstractWrapper {
}
return result;
}
-
- /**
- * Retrieve the underlying GameProfile.
- * @return The GameProfile.
- */
- private GameProfile getProfile() {
- return (GameProfile) handle;
- }
/**
* Construct a new game profile with the same ID, but different name.
+ *
* @param name - the new name of the profile to create.
* @return The new game profile.
*/
public WrappedGameProfile withName(String name) {
return new WrappedGameProfile(getId(), name);
}
-
+
/**
* Construct a new game profile with the same name, but different id.
+ *
* @param id - the new id of the profile to create.
* @return The new game profile.
*/
public WrappedGameProfile withId(String id) {
return new WrappedGameProfile(id, getName());
}
-
+
/**
* Determine if the game profile contains both an UUID and a name.
+ *
* @return TRUE if it does, FALSE otherwise.
*/
public boolean isComplete() {
- return getProfile().isComplete();
+ return (Boolean) IS_COMPLETE.invoke(handle);
}
-
+
@Override
public String toString() {
- return String.valueOf(getProfile());
+ return String.valueOf(getHandle());
}
-
+
@Override
public int hashCode() {
// Mojang's hashCode() is broken, it doesn't handle NULL id or name. So we implement our own
return Objects.hashCode(getId(), getName());
}
-
+
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
-
+
if (obj instanceof WrappedGameProfile) {
WrappedGameProfile other = (WrappedGameProfile) obj;
- return Objects.equal(getProfile(), other.getProfile());
+ return Objects.equal(getHandle(), other.getHandle());
}
+
return false;
}
-}
+}
\ No newline at end of file