Add backwards compatibility for versions 1.0 thru 1.7.10
Dieser Commit ist enthalten in:
Ursprung
2c743e1c89
Commit
addfacb19c
2
ProtocolLib/.gitignore
vendored
2
ProtocolLib/.gitignore
vendored
@ -163,3 +163,5 @@ pip-log.txt
|
|||||||
|
|
||||||
# Mac crap
|
# Mac crap
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
/target
|
||||||
|
/target
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>cp1252</project.build.sourceEncoding>
|
<project.build.sourceEncoding>cp1252</project.build.sourceEncoding>
|
||||||
<powermock.version>1.5</powermock.version>
|
<jarName>ProtocolLib</jarName>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
@ -75,6 +75,7 @@
|
|||||||
<exclude>org.spigotmc:spigot</exclude>
|
<exclude>org.spigotmc:spigot</exclude>
|
||||||
<exclude>org.spigotmc:spigot-api</exclude>
|
<exclude>org.spigotmc:spigot-api</exclude>
|
||||||
<exclude>junit:junit</exclude>
|
<exclude>junit:junit</exclude>
|
||||||
|
<exclude>com.google*</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</artifactSet>
|
</artifactSet>
|
||||||
</configuration>
|
</configuration>
|
||||||
@ -99,7 +100,7 @@
|
|||||||
<archive>
|
<archive>
|
||||||
<addMavenDescriptor>false</addMavenDescriptor>
|
<addMavenDescriptor>false</addMavenDescriptor>
|
||||||
</archive>
|
</archive>
|
||||||
<finalName>${project.name}</finalName>
|
<finalName>${jarName}</finalName>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
@ -186,6 +187,62 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>backwards-compat</id>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<jarName>ProtocolLib-Legacy</jarName>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>2.3</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||||
|
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||||
|
|
||||||
|
<relocations>
|
||||||
|
<relocation>
|
||||||
|
<pattern>net.sf</pattern>
|
||||||
|
<shadedPattern>com.comphenix.net.sf</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
<relocation>
|
||||||
|
<pattern>com.google.common</pattern>
|
||||||
|
<shadedPattern>com.comphenix.protocol.compat.com.google.common</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
<relocation>
|
||||||
|
<pattern>io.netty</pattern>
|
||||||
|
<shadedPattern>net.minecraft.util.io.netty</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
</relocations>
|
||||||
|
|
||||||
|
<artifactSet>
|
||||||
|
<excludes>
|
||||||
|
<exclude>org.spigotmc:spigot</exclude>
|
||||||
|
<exclude>org.spigotmc:spigot-api</exclude>
|
||||||
|
<exclude>junit:junit</exclude>
|
||||||
|
</excludes>
|
||||||
|
</artifactSet>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
@ -264,14 +321,24 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.powermock</groupId>
|
<groupId>org.powermock</groupId>
|
||||||
<artifactId>powermock-module-junit4</artifactId>
|
<artifactId>powermock-module-junit4</artifactId>
|
||||||
<version>${powermock.version}</version>
|
<version>1.5</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.powermock</groupId>
|
<groupId>org.powermock</groupId>
|
||||||
<artifactId>powermock-api-mockito</artifactId>
|
<artifactId>powermock-api-mockito</artifactId>
|
||||||
<version>${powermock.version}</version>
|
<version>1.5</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>17.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-all</artifactId>
|
||||||
|
<version>4.0.26.Final</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
@ -83,7 +83,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
/**
|
/**
|
||||||
* The minimum version ProtocolLib has been tested with.
|
* The minimum version ProtocolLib has been tested with.
|
||||||
*/
|
*/
|
||||||
public static final String MINIMUM_MINECRAFT_VERSION = "1.8";
|
public static final String MINIMUM_MINECRAFT_VERSION = "1.0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum version ProtocolLib has been tested with,
|
* The maximum version ProtocolLib has been tested with,
|
||||||
|
@ -50,9 +50,9 @@ import com.comphenix.protocol.utility.MinecraftFields;
|
|||||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||||
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
|
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.MapMaker;
|
import com.google.common.collect.MapMaker;
|
||||||
import com.mojang.authlib.GameProfile;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a channel injector.
|
* Represents a channel injector.
|
||||||
@ -563,13 +563,14 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
|||||||
PACKET_LOGIN_CLIENT = loginClass;
|
PACKET_LOGIN_CLIENT = loginClass;
|
||||||
}
|
}
|
||||||
if (loginClient == null) {
|
if (loginClient == null) {
|
||||||
loginClient = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, GameProfile.class, true);
|
loginClient = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, MinecraftReflection.getGameProfileClass(), true);
|
||||||
LOGIN_GAME_PROFILE = loginClient;
|
LOGIN_GAME_PROFILE = loginClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if we are dealing with the login packet
|
// See if we are dealing with the login packet
|
||||||
if (loginClass.equals(packetClass)) {
|
if (loginClass.equals(packetClass)) {
|
||||||
GameProfile profile = (GameProfile) loginClient.get(packet);
|
// GameProfile profile = (GameProfile) loginClient.get(packet);
|
||||||
|
WrappedGameProfile profile = WrappedGameProfile.fromHandle(loginClient.get(packet));
|
||||||
|
|
||||||
// Save the channel injector
|
// Save the channel injector
|
||||||
factory.cacheInjector(profile.getName(), this);
|
factory.cacheInjector(profile.getName(), this);
|
||||||
|
@ -12,8 +12,10 @@ import com.comphenix.protocol.PacketType.Sender;
|
|||||||
import com.comphenix.protocol.injector.packet.MapContainer;
|
import com.comphenix.protocol.injector.packet.MapContainer;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||||
import com.google.common.collect.BiMap;
|
import com.google.common.collect.BiMap;
|
||||||
import com.google.common.collect.HashBiMap;
|
import com.google.common.collect.HashBiMap;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
@ -109,6 +111,12 @@ public class NettyProtocolRegistry {
|
|||||||
private synchronized void initialize() {
|
private synchronized void initialize() {
|
||||||
Object[] protocols = enumProtocol.getEnumConstants();
|
Object[] protocols = enumProtocol.getEnumConstants();
|
||||||
|
|
||||||
|
// TODO: Fins a better less than 1.7 check
|
||||||
|
if (MinecraftVersion.getCurrentVersion().compareTo(MinecraftVersion.BOUNTIFUL_UPDATE) <= 0) {
|
||||||
|
initialize17();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// ID to Packet class maps
|
// ID to Packet class maps
|
||||||
Map<Object, Map<Integer, Class<?>>> serverMaps = Maps.newLinkedHashMap();
|
Map<Object, Map<Integer, Class<?>>> serverMaps = Maps.newLinkedHashMap();
|
||||||
Map<Object, Map<Integer, Class<?>>> clientMaps = Maps.newLinkedHashMap();
|
Map<Object, Map<Integer, Class<?>>> clientMaps = Maps.newLinkedHashMap();
|
||||||
@ -140,13 +148,13 @@ public class NettyProtocolRegistry {
|
|||||||
result.containers.add(new MapContainer(map));
|
result.containers.add(new MapContainer(map));
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Heuristic - there are more server packets than client packets
|
// Heuristic - there are more server packets than client packets
|
||||||
// if (sum(clientMaps) > sum(serverMaps)) {
|
/* if (sum(clientMaps) > sum(serverMaps)) {
|
||||||
// // Swap if this is violated
|
// Swap if this is violated
|
||||||
// List<Map<Integer, Class<?>>> temp = serverMaps;
|
List<Map<Integer, Class<?>>> temp = serverMaps;
|
||||||
// serverMaps = clientMaps;
|
serverMaps = clientMaps;
|
||||||
// clientMaps = temp;
|
clientMaps = temp;
|
||||||
// }
|
} */
|
||||||
|
|
||||||
for (int i = 0; i < protocols.length; i++) {
|
for (int i = 0; i < protocols.length; i++) {
|
||||||
Object protocol = protocols[i];
|
Object protocol = protocols[i];
|
||||||
@ -159,6 +167,50 @@ public class NettyProtocolRegistry {
|
|||||||
if (clientMaps.containsKey(protocol))
|
if (clientMaps.containsKey(protocol))
|
||||||
associatePackets(result, clientMaps.get(protocol), equivalent, Sender.CLIENT);
|
associatePackets(result, clientMaps.get(protocol), equivalent, Sender.CLIENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exchange (thread safe, as we have only one writer)
|
||||||
|
this.register = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void initialize17() {
|
||||||
|
final Object[] protocols = enumProtocol.getEnumConstants();
|
||||||
|
List<Map<Integer, Class<?>>> serverMaps = Lists.newArrayList();
|
||||||
|
List<Map<Integer, Class<?>>> clientMaps = Lists.newArrayList();
|
||||||
|
StructureModifier<Object> modifier = null;
|
||||||
|
|
||||||
|
// Result
|
||||||
|
Register result = new Register();
|
||||||
|
|
||||||
|
for (Object protocol : protocols) {
|
||||||
|
if (modifier == null)
|
||||||
|
modifier = new StructureModifier<Object>(protocol.getClass().getSuperclass(), false);
|
||||||
|
StructureModifier<Map<Integer, Class<?>>> maps = modifier.withTarget(protocol).withType(Map.class);
|
||||||
|
|
||||||
|
serverMaps.add(maps.read(0));
|
||||||
|
clientMaps.add(maps.read(1));
|
||||||
|
}
|
||||||
|
// Maps we have to occationally check have changed
|
||||||
|
for (Map<Integer, Class<?>> map : Iterables.concat(serverMaps, clientMaps)) {
|
||||||
|
result.containers.add(new MapContainer(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heuristic - there are more server packets than client packets
|
||||||
|
if (sum(clientMaps) > sum(serverMaps)) {
|
||||||
|
// Swap if this is violated
|
||||||
|
List<Map<Integer, Class<?>>> temp = serverMaps;
|
||||||
|
serverMaps = clientMaps;
|
||||||
|
clientMaps = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < protocols.length; i++) {
|
||||||
|
Enum<?> enumProtocol = (Enum<?>) protocols[i];
|
||||||
|
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
|
||||||
|
|
||||||
|
// Associate known types
|
||||||
|
associatePackets(result, serverMaps.get(i), equivalent, Sender.SERVER);
|
||||||
|
associatePackets(result, clientMaps.get(i), equivalent, Sender.CLIENT);
|
||||||
|
}
|
||||||
|
|
||||||
// Exchange (thread safe, as we have only one writer)
|
// Exchange (thread safe, as we have only one writer)
|
||||||
this.register = result;
|
this.register = result;
|
||||||
}
|
}
|
||||||
@ -175,16 +227,16 @@ public class NettyProtocolRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * Retrieve the number of mapping in all the maps.
|
* Retrieve the number of mapping in all the maps.
|
||||||
// * @param maps - iterable of maps.
|
* @param maps - iterable of maps.
|
||||||
// * @return The sum of all the entries.
|
* @return The sum of all the entries.
|
||||||
// */
|
*/
|
||||||
// private int sum(Iterable<? extends Map<Integer, Class<?>>> maps) {
|
private int sum(Iterable<? extends Map<Integer, Class<?>>> maps) {
|
||||||
// int count = 0;
|
int count = 0;
|
||||||
//
|
|
||||||
// for (Map<Integer, Class<?>> map : maps)
|
for (Map<Integer, Class<?>> map : maps)
|
||||||
// count += map.size();
|
count += map.size();
|
||||||
// return count;
|
return count;
|
||||||
// }
|
}
|
||||||
}
|
}
|
@ -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.injector.netty;
|
package com.comphenix.protocol.injector.netty;
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ public final class Accessors {
|
|||||||
* @return The field accessor.
|
* @return The field accessor.
|
||||||
* @throws IllegalArgumentException If the field cannot be found.
|
* @throws IllegalArgumentException If the field cannot be found.
|
||||||
*/
|
*/
|
||||||
public static FieldAccessor getFieldAccessor(Class<?> instanceClass, Class<?> fieldClass, boolean forceAccess) {
|
public static FieldAccessor getFieldAccessor(Class<?> instanceClass, Class<?> fieldClass, boolean forceAccess) {
|
||||||
// Get a field accessor
|
// Get a field accessor
|
||||||
Field field = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldByType(null, fieldClass);
|
Field field = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldByType(null, fieldClass);
|
||||||
return Accessors.getFieldAccessor(field);
|
return Accessors.getFieldAccessor(field);
|
||||||
@ -65,7 +65,7 @@ public final class Accessors {
|
|||||||
* @param forceAccess - whether or not to look for private and protected fields.
|
* @param forceAccess - whether or not to look for private and protected fields.
|
||||||
* @return The accessors.
|
* @return The accessors.
|
||||||
*/
|
*/
|
||||||
public static FieldAccessor[] getFieldAccessorArray(Class<?> instanceClass, Class<?> fieldClass, boolean forceAccess) {
|
public static FieldAccessor[] getFieldAccessorArray(Class<?> instanceClass, Class<?> fieldClass, boolean forceAccess) {
|
||||||
List<Field> fields = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldListByType(fieldClass);
|
List<Field> fields = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldListByType(fieldClass);
|
||||||
FieldAccessor[] accessors = new FieldAccessor[fields.size()];
|
FieldAccessor[] accessors = new FieldAccessor[fields.size()];
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ public final class Accessors {
|
|||||||
* @return The value of that field.
|
* @return The value of that field.
|
||||||
* @throws IllegalArgumentException If the field cannot be found.
|
* @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));
|
return Accessors.getFieldAccessor(ExactReflection.fromClass(instanceClass, true).getField(fieldName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,13 +120,27 @@ public final class Accessors {
|
|||||||
|
|
||||||
// Verify the type
|
// Verify the type
|
||||||
if (fieldType.isAssignableFrom(accessor.getField().getType())) {
|
if (fieldType.isAssignableFrom(accessor.getField().getType())) {
|
||||||
return accessor;
|
return accessor;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
return null;
|
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.
|
* 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.
|
* Retrieve a field accessor that will cache the content of the field.
|
||||||
* <p>
|
* <p>
|
||||||
* 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.
|
* so it's best to use this on final fields.
|
||||||
* @param inner - the accessor.
|
* @param inner - the accessor.
|
||||||
* @return A cached field accessor.
|
* @return A cached field accessor.
|
||||||
|
@ -40,12 +40,21 @@ public class BukkitCloner implements Cloner {
|
|||||||
List<Class<?>> classes = Lists.newArrayList();
|
List<Class<?>> classes = Lists.newArrayList();
|
||||||
|
|
||||||
classes.add(MinecraftReflection.getItemStackClass());
|
classes.add(MinecraftReflection.getItemStackClass());
|
||||||
classes.add(MinecraftReflection.getBlockPositionClass());
|
|
||||||
classes.add(MinecraftReflection.getDataWatcherClass());
|
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()) {
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
classes.add(MinecraftReflection.getServerPingClass());
|
classes.add(MinecraftReflection.getServerPingClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clonableClasses = classes.toArray(new Class<?>[0]);
|
this.clonableClasses = classes.toArray(new Class<?>[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +290,6 @@ public class DefaultInstances implements InstanceProvider {
|
|||||||
|
|
||||||
return createInstance(type, minimum, types, params);
|
return createInstance(type, minimum, types, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Nope, we couldn't create this type. Might for instance be NotConstructableException.
|
// Nope, we couldn't create this type. Might for instance be NotConstructableException.
|
||||||
}
|
}
|
||||||
|
@ -596,8 +596,17 @@ public class MinecraftReflection {
|
|||||||
if (!isUsingNetty())
|
if (!isUsingNetty())
|
||||||
throw new IllegalStateException("GameProfile does not exist in version 1.6.4 and earlier.");
|
throw new IllegalStateException("GameProfile does not exist in version 1.6.4 and earlier.");
|
||||||
|
|
||||||
// Yay, we can actually refer to it directly
|
try {
|
||||||
return GameProfile.class;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
package com.comphenix.protocol.wrappers;
|
||||||
|
|
||||||
|
@ -31,24 +31,29 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
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.ProtocolLibrary;
|
||||||
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.
|
||||||
@ -56,127 +61,127 @@ import com.google.common.collect.Iterators;
|
|||||||
* @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
|
||||||
// */
|
*/
|
||||||
// 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) {
|
||||||
// ProtocolLibrary.log(Level.WARNING, "Unable to find " + className);
|
ProtocolLibrary.log(Level.WARNING, "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) {
|
public static CustomType fromValue(Object value) {
|
||||||
// for (CustomType type : CustomType.values()) {
|
for (CustomType type : CustomType.values()) {
|
||||||
// if (type.getSpigotClass().isInstance(value)) {
|
if (type.getSpigotClass().isInstance(value)) {
|
||||||
// return type;
|
return type;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// return null;
|
return null;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to assign integer IDs to given types.
|
* Used to assign integer IDs to given types.
|
||||||
@ -601,20 +606,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.
|
||||||
// */
|
*/
|
||||||
// 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
|
||||||
@ -718,7 +723,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);
|
||||||
@ -737,17 +742,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) {
|
||||||
|
@ -14,13 +14,12 @@ import com.comphenix.protocol.injector.BukkitUnwrapper;
|
|||||||
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.ConstructorAccessor;
|
||||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||||
|
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
import com.comphenix.protocol.wrappers.collection.ConvertedMultimap;
|
import com.comphenix.protocol.wrappers.collection.ConvertedMultimap;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.mojang.authlib.GameProfile;
|
|
||||||
import com.mojang.authlib.properties.Property;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a wrapper for a game profile.
|
* Represents a wrapper for a game profile.
|
||||||
@ -28,62 +27,79 @@ import com.mojang.authlib.properties.Property;
|
|||||||
*/
|
*/
|
||||||
public class WrappedGameProfile extends AbstractWrapper {
|
public class WrappedGameProfile extends AbstractWrapper {
|
||||||
public static final ReportType REPORT_INVALID_UUID = new ReportType("Plugin %s created a profile with '%s' as an UUID.");
|
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 Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
|
||||||
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 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
|
// Fetching game profile
|
||||||
private static FieldAccessor PLAYER_PROFILE;
|
private static FieldAccessor PLAYER_PROFILE;
|
||||||
private static FieldAccessor OFFLINE_PROFILE;
|
private static FieldAccessor OFFLINE_PROFILE;
|
||||||
|
|
||||||
// Property map
|
// Property map
|
||||||
private Multimap<String, WrappedSignedProperty> propertyMap;
|
private Multimap<String, WrappedSignedProperty> propertyMap;
|
||||||
|
|
||||||
// Parsed UUID
|
// Parsed UUID
|
||||||
private volatile UUID parsedUUID;
|
private volatile UUID parsedUUID;
|
||||||
|
|
||||||
// Profile from a handle
|
// Profile from a handle
|
||||||
private WrappedGameProfile(Object profile) {
|
private WrappedGameProfile(Object profile) {
|
||||||
super(GameProfile.class);
|
super(GAME_PROFILE);
|
||||||
setHandle(profile);
|
setHandle(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the associated game profile of a player.
|
* Retrieve the associated game profile of a player.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that this may not exist in the current Minecraft version.
|
* Note that this may not exist in the current Minecraft version.
|
||||||
|
*
|
||||||
* @param player - the player.
|
* @param player - the player.
|
||||||
* @return The game profile.
|
* @return The game profile.
|
||||||
*/
|
*/
|
||||||
public static WrappedGameProfile fromPlayer(Player player) {
|
public static WrappedGameProfile fromPlayer(Player player) {
|
||||||
FieldAccessor accessor = PLAYER_PROFILE;
|
FieldAccessor accessor = PLAYER_PROFILE;
|
||||||
Object nmsPlayer = BukkitUnwrapper.getInstance().unwrapItem(player);
|
|
||||||
|
|
||||||
if (accessor == null) {
|
if (accessor == null) {
|
||||||
accessor = Accessors.getFieldAccessor(MinecraftReflection.getEntityHumanClass(), GameProfile.class, true);
|
accessor = Accessors.getFieldAccessor(MinecraftReflection.getEntityHumanClass(), GAME_PROFILE, true);
|
||||||
PLAYER_PROFILE = accessor;
|
PLAYER_PROFILE = accessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object nmsPlayer = BukkitUnwrapper.getInstance().unwrapItem(player);
|
||||||
return WrappedGameProfile.fromHandle(PLAYER_PROFILE.get(nmsPlayer));
|
return WrappedGameProfile.fromHandle(PLAYER_PROFILE.get(nmsPlayer));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the associated game profile of an offline player.
|
* Retrieve the associated game profile of an offline player.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that this may not exist in the current Minecraft version.
|
* Note that this may not exist in the current Minecraft version.
|
||||||
|
*
|
||||||
* @param player - the offline player.
|
* @param player - the offline player.
|
||||||
* @return The game profile.
|
* @return The game profile.
|
||||||
*/
|
*/
|
||||||
public static WrappedGameProfile fromOfflinePlayer(OfflinePlayer player) {
|
public static WrappedGameProfile fromOfflinePlayer(OfflinePlayer player) {
|
||||||
FieldAccessor accessor = OFFLINE_PROFILE;
|
FieldAccessor accessor = OFFLINE_PROFILE;
|
||||||
|
|
||||||
if (accessor == null) {
|
if (accessor == null) {
|
||||||
accessor = Accessors.getFieldAccessor(player.getClass(), GameProfile.class, true);
|
accessor = Accessors.getFieldAccessor(player.getClass(), GAME_PROFILE, true);
|
||||||
OFFLINE_PROFILE = accessor;
|
OFFLINE_PROFILE = accessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
return WrappedGameProfile.fromHandle(OFFLINE_PROFILE.get(player));
|
return WrappedGameProfile.fromHandle(OFFLINE_PROFILE.get(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new game profile with the given properties.
|
* Construct a new game profile with the given properties.
|
||||||
* <p>
|
* <p>
|
||||||
@ -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.
|
* IDs that cannot be parsed as an UUID will be hashed and form a version 3 UUID instead.
|
||||||
* <p>
|
* <p>
|
||||||
* This method is deprecated for Minecraft 1.7.8 and above.
|
* This method is deprecated for Minecraft 1.7.8 and above.
|
||||||
|
*
|
||||||
* @param id - the UUID of the player.
|
* @param id - the UUID of the player.
|
||||||
* @param name - the name of the player.
|
* @param name - the name of the player.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public WrappedGameProfile(String id, String name) {
|
public WrappedGameProfile(String id, String name) {
|
||||||
super(GameProfile.class);
|
super(GAME_PROFILE);
|
||||||
|
|
||||||
if (CREATE_STRING_STRING != null) {
|
if (CREATE_STRING_STRING != null) {
|
||||||
setHandle(CREATE_STRING_STRING.invoke(id, name));
|
setHandle(CREATE_STRING_STRING.invoke(id, name));
|
||||||
|
} else if (CREATE_UUID_STRING != null) {
|
||||||
|
setHandle(CREATE_UUID_STRING.invoke(parseUUID(id), name));
|
||||||
} else {
|
} else {
|
||||||
setHandle(new GameProfile(parseUUID(id), name));
|
throw new IllegalArgumentException("Unsupported GameProfile constructor.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new game profile with the given properties.
|
* Construct a new game profile with the given properties.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that at least one of the parameters must be non-null.
|
* Note that at least one of the parameters must be non-null.
|
||||||
|
*
|
||||||
* @param uuid - the UUID of the player, or NULL.
|
* @param uuid - the UUID of the player, or NULL.
|
||||||
* @param name - the name of the player, or NULL.
|
* @param name - the name of the player, or NULL.
|
||||||
*/
|
*/
|
||||||
public WrappedGameProfile(UUID uuid, String name) {
|
public WrappedGameProfile(UUID uuid, String name) {
|
||||||
super(GameProfile.class);
|
super(GAME_PROFILE);
|
||||||
|
|
||||||
if (CREATE_STRING_STRING != null) {
|
if (CREATE_STRING_STRING != null) {
|
||||||
setHandle(CREATE_STRING_STRING.invoke(uuid != null ? uuid.toString() : null, name));
|
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 {
|
} else {
|
||||||
setHandle(new GameProfile(uuid, name));
|
throw new IllegalArgumentException("Unsupported GameProfile constructor.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a wrapper around an existing game profile.
|
* Construct a wrapper around an existing game profile.
|
||||||
|
*
|
||||||
* @param profile - the underlying profile, or NULL.
|
* @param profile - the underlying profile, or NULL.
|
||||||
*/
|
*/
|
||||||
public static WrappedGameProfile fromHandle(Object handle) {
|
public static WrappedGameProfile fromHandle(Object handle) {
|
||||||
if (handle == null)
|
if (handle == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return new WrappedGameProfile(handle);
|
return new WrappedGameProfile(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse an UUID using very lax rules, as specified in {@link #WrappedGameProfile(UUID, String)}.
|
* Parse an UUID using very lax rules, as specified in {@link #WrappedGameProfile(UUID, String)}.
|
||||||
|
*
|
||||||
* @param id - text.
|
* @param id - text.
|
||||||
* @return The corresponding UUID.
|
* @return The corresponding UUID.
|
||||||
* @throws IllegalArgumentException If we cannot parse the text.
|
* @throws IllegalArgumentException If we cannot parse the text.
|
||||||
@ -143,13 +168,10 @@ public class WrappedGameProfile extends AbstractWrapper {
|
|||||||
return id != null ? UUID.fromString(id) : null;
|
return id != null ? UUID.fromString(id) : null;
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// Warn once every hour (per plugin)
|
// Warn once every hour (per plugin)
|
||||||
ProtocolLibrary.getErrorReporter().reportWarning(
|
ProtocolLibrary.getErrorReporter()
|
||||||
WrappedGameProfile.class,
|
.reportWarning(WrappedGameProfile.class, Report.newBuilder(REPORT_INVALID_UUID)
|
||||||
Report.newBuilder(REPORT_INVALID_UUID).
|
.rateLimit(1, TimeUnit.HOURS)
|
||||||
rateLimit(1, TimeUnit.HOURS).
|
.messageParam(PluginContext.getPluginCaller(new Exception()), id));
|
||||||
messageParam(PluginContext.getPluginCaller(new Exception()), id)
|
|
||||||
);
|
|
||||||
|
|
||||||
return UUID.nameUUIDFromBytes(id.getBytes(Charsets.UTF_8));
|
return UUID.nameUUIDFromBytes(id.getBytes(Charsets.UTF_8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,70 +179,86 @@ public class WrappedGameProfile extends AbstractWrapper {
|
|||||||
/**
|
/**
|
||||||
* Retrieve the UUID of the player.
|
* Retrieve the UUID of the player.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that Minecraft 1.7.5 and earlier doesn't use UUIDs internally, and it may not be possible
|
* 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.
|
||||||
* to convert the string to an UUID.
|
|
||||||
* <p>
|
* <p>
|
||||||
* We use the same lax conversion as in {@link #WrappedGameProfile(String, String)}.
|
* We use the same lax conversion as in {@link #WrappedGameProfile(String, String)}.
|
||||||
|
*
|
||||||
* @return The UUID, or NULL if the UUID is NULL.
|
* @return The UUID, or NULL if the UUID is NULL.
|
||||||
* @throws IllegalStateException If we cannot parse the internal ID as an UUID.
|
* @throws IllegalStateException If we cannot parse the internal ID as an UUID.
|
||||||
*/
|
*/
|
||||||
public UUID getUUID() {
|
public UUID getUUID() {
|
||||||
UUID uuid = parsedUUID;
|
UUID uuid = parsedUUID;
|
||||||
|
|
||||||
if (uuid == null) {
|
if (uuid == null) {
|
||||||
try {
|
try {
|
||||||
if (GET_UUID_STRING != null) {
|
if (GET_UUID_STRING != null) {
|
||||||
uuid = parseUUID(getId());
|
uuid = parseUUID(getId());
|
||||||
|
} else if (GET_ID != null) {
|
||||||
|
uuid = (UUID) GET_ID.invoke(handle);
|
||||||
} else {
|
} else {
|
||||||
uuid = getProfile().getId();
|
throw new IllegalStateException("Unsupported getId() method");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache for later
|
// Cache for later
|
||||||
parsedUUID = uuid;
|
parsedUUID = uuid;
|
||||||
} catch (IllegalArgumentException e) {
|
} 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;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the textual representation of the player's UUID.
|
* Retrieve the textual representation of the player's UUID.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that there's nothing stopping plugins from creating non-standard UUIDs.
|
* Note that there's nothing stopping plugins from creating non-standard UUIDs.
|
||||||
* <p>
|
* <p>
|
||||||
* In Minecraft 1.7.8 and later, this simply returns the string form of {@link #getUUID()}.
|
* 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.
|
* @return The UUID of the player, or NULL if not computed.
|
||||||
*/
|
*/
|
||||||
public String getId() {
|
public String getId() {
|
||||||
if (GET_UUID_STRING != null)
|
if (GET_UUID_STRING != null) {
|
||||||
return (String) GET_UUID_STRING.get(handle);
|
return (String) GET_UUID_STRING.get(handle);
|
||||||
final GameProfile profile = getProfile();
|
} else if (GET_ID != null) {
|
||||||
return profile.getId() != null ? profile.getId().toString() : 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.
|
* Retrieve the name of the player.
|
||||||
|
*
|
||||||
* @return The player name.
|
* @return The player name.
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
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.
|
* Retrieve the property map of signed values.
|
||||||
|
*
|
||||||
* @return Property map.
|
* @return Property map.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
public Multimap<String, WrappedSignedProperty> getProperties() {
|
public Multimap<String, WrappedSignedProperty> getProperties() {
|
||||||
Multimap<String, WrappedSignedProperty> result = propertyMap;
|
Multimap<String, WrappedSignedProperty> result = propertyMap;
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = new ConvertedMultimap<String, Property, WrappedSignedProperty>(
|
Multimap properties = (Multimap) GET_PROPERTIES.invoke(handle);
|
||||||
GuavaWrappers.getBukkitMultimap(getProfile().getProperties())) {
|
result = new ConvertedMultimap<String, Object, WrappedSignedProperty>(GuavaWrappers.getBukkitMultimap(properties)) {
|
||||||
@Override
|
@Override
|
||||||
protected Property toInner(WrappedSignedProperty outer) {
|
protected Object toInner(WrappedSignedProperty outer) {
|
||||||
return (Property) outer.handle;
|
return outer.handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object toInnerObject(Object outer) {
|
protected Object toInnerObject(Object outer) {
|
||||||
if (outer instanceof WrappedSignedProperty) {
|
if (outer instanceof WrappedSignedProperty) {
|
||||||
@ -228,9 +266,9 @@ public class WrappedGameProfile extends AbstractWrapper {
|
|||||||
}
|
}
|
||||||
return outer;
|
return outer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected WrappedSignedProperty toOuter(Property inner) {
|
protected WrappedSignedProperty toOuter(Object inner) {
|
||||||
return WrappedSignedProperty.fromHandle(inner);
|
return WrappedSignedProperty.fromHandle(inner);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -238,61 +276,57 @@ public class WrappedGameProfile extends AbstractWrapper {
|
|||||||
}
|
}
|
||||||
return result;
|
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.
|
* Construct a new game profile with the same ID, but different name.
|
||||||
|
*
|
||||||
* @param name - the new name of the profile to create.
|
* @param name - the new name of the profile to create.
|
||||||
* @return The new game profile.
|
* @return The new game profile.
|
||||||
*/
|
*/
|
||||||
public WrappedGameProfile withName(String name) {
|
public WrappedGameProfile withName(String name) {
|
||||||
return new WrappedGameProfile(getId(), name);
|
return new WrappedGameProfile(getId(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new game profile with the same name, but different id.
|
* Construct a new game profile with the same name, but different id.
|
||||||
|
*
|
||||||
* @param id - the new id of the profile to create.
|
* @param id - the new id of the profile to create.
|
||||||
* @return The new game profile.
|
* @return The new game profile.
|
||||||
*/
|
*/
|
||||||
public WrappedGameProfile withId(String id) {
|
public WrappedGameProfile withId(String id) {
|
||||||
return new WrappedGameProfile(id, getName());
|
return new WrappedGameProfile(id, getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the game profile contains both an UUID and a name.
|
* Determine if the game profile contains both an UUID and a name.
|
||||||
|
*
|
||||||
* @return TRUE if it does, FALSE otherwise.
|
* @return TRUE if it does, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isComplete() {
|
public boolean isComplete() {
|
||||||
return getProfile().isComplete();
|
return (Boolean) IS_COMPLETE.invoke(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.valueOf(getProfile());
|
return String.valueOf(getHandle());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
// Mojang's hashCode() is broken, it doesn't handle NULL id or name. So we implement our own
|
// Mojang's hashCode() is broken, it doesn't handle NULL id or name. So we implement our own
|
||||||
return Objects.hashCode(getId(), getName());
|
return Objects.hashCode(getId(), getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj == this)
|
if (obj == this)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (obj instanceof WrappedGameProfile) {
|
if (obj instanceof WrappedGameProfile) {
|
||||||
WrappedGameProfile other = (WrappedGameProfile) obj;
|
WrappedGameProfile other = (WrappedGameProfile) obj;
|
||||||
return Objects.equal(getProfile(), other.getProfile());
|
return Objects.equal(getHandle(), other.getHandle());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
In neuem Issue referenzieren
Einen Benutzer sperren