Updating to 1.7.8

This was necessitated by two new NMS changes:
* NBTCompressedStreamTools.a(DataInput, int) now includes an additional
  parameter NBTReadLimiter
* GameProfile changed the type of getId() from String to UUID, along
  with the constructor (String, String) to (UUID, String).
This commit is contained in:
Kristian S. Stangeland 2014-04-12 23:56:11 +02:00
parent c84a5d7fa2
commit e0b0e51342
19 changed files with 228 additions and 59 deletions

View File

@ -232,7 +232,7 @@
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>1.7.2-R0.1-SNAPSHOT</version>
<version>1.7.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -23,6 +23,7 @@ import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.sf.cglib.proxy.Enhancer;
@ -53,6 +54,7 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
// Relevant data about an offline player
private String name;
private UUID uuid;
private long firstPlayed;
private long lastPlayed;
private boolean operator;
@ -77,6 +79,7 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
*/
public SerializedOfflinePlayer(OfflinePlayer offline) {
this.name = offline.getName();
this.uuid = offline.getUniqueId();
this.firstPlayed = offline.getFirstPlayed();
this.lastPlayed = offline.getLastPlayed();
this.operator = offline.isOp();
@ -116,6 +119,11 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return lastPlayed;
}
@Override
public UUID getUniqueId() {
return uuid;
}
@Override
public String getName() {
return name;

View File

@ -132,7 +132,7 @@ public class FuzzyReflection {
} catch (IllegalArgumentException e) {
// Try getting the field instead
// Note that this will throw an exception if not found
field = getFieldByType("instance", source.getClass());
field = getFieldByType("instance", source);
}
// Convert into unchecked exceptions

View File

@ -106,6 +106,41 @@ public final class Accessors {
return new DefaultFieldAccessor(field);
}
/**
* Retrieve a field accessor for a field with the given name and equivalent type, or NULL.
* @param clazz - the declaration class.
* @param fieldName - the field name.
* @param fieldType - assignable field type.
* @return The field accessor, or NULL if not found.
*/
public static FieldAccessor getFieldAcccessorOrNull(Class<?> clazz, String fieldName, Class<?> fieldType) {
try {
FieldAccessor accessor = Accessors.getFieldAccessor(clazz, fieldName, true);
// Verify the type
if (fieldType.isAssignableFrom(accessor.getField().getType())) {
return accessor;
}
return null;
} catch (IllegalArgumentException e) {
return null;
}
}
/**
* Find a specific constructor in a class.
* @param clazz - the class.
* @param parameters - the signature of the constructor to find.
* @return The constructor, or NULL if not found.
*/
public static ConstructorAccessor getConstructorAccessorOrNull(Class<?> clazz, Class<?>... parameters) {
try {
return Accessors.getConstructorAccessor(clazz, parameters);
} catch (IllegalArgumentException e) {
return null; // Not found
}
}
/**
* Retrieve a field accessor that will cache the content of the field.
* <p>
@ -198,6 +233,8 @@ public final class Accessors {
* @param instanceClass - the parent class.
* @param parameters - the parameters.
* @return The constructor accessor.
* @throws IllegalArgumentException If we cannot find this constructor.
* @throws IllegalStateException If we cannot access reflection.
*/
public static ConstructorAccessor getConstructorAccessor(Class<?> instanceClass, Class<?>... parameters) {
try {

View File

@ -1327,6 +1327,16 @@ public class MinecraftReflection {
}
}
/**
* Retrieve the NBT read limiter class.
* <p>
* This is only supported in 1.7.8 (released 2014) and higher.
* @return The NBT read limiter.
*/
public static Class<?> getNBTReadLimiterClass() {
return getMinecraftClass("NBTReadLimiter");
}
/**
* Retrieve the NBT Compound class.
* @return The NBT Compond class.

View File

@ -43,6 +43,11 @@ public class MinecraftVersion implements Comparable<MinecraftVersion>, Serializa
*/
private static final String VERSION_PATTERN = ".*\\(.*MC.\\s*([a-zA-z0-9\\-\\.]+)\\s*\\)";
/**
* Version 1.7.8 - the update that changed the skin format (and distribution - R.I.P. player disguise)
*/
public static final MinecraftVersion SKIN_UPDATE = new MinecraftVersion("1.7.8");
/**
* Version 1.7.2 - the update that changed the world.
*/

View File

@ -1,6 +1,14 @@
package com.comphenix.protocol.wrappers;
import java.util.UUID;
import org.apache.commons.lang.StringUtils;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import net.minecraft.util.com.mojang.authlib.GameProfile;
@ -9,6 +17,10 @@ import net.minecraft.util.com.mojang.authlib.GameProfile;
* @author Kristian
*/
public class WrappedGameProfile extends AbstractWrapper {
// 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);
// Profile from a handle
private WrappedGameProfile(Object profile) {
super(GameProfile.class);
@ -21,7 +33,43 @@ public class WrappedGameProfile extends AbstractWrapper {
* @param name - the name of the player.
*/
public WrappedGameProfile(String id, String name) {
this(new GameProfile(id, name));
super(GameProfile.class);
if (CREATE_STRING_STRING != null) {
setHandle(CREATE_STRING_STRING.invoke(id, name));
} else {
parseUUID(id, name);
setHandle(new GameProfile(parseUUID(id, name), name));
}
}
private UUID parseUUID(String id, String name) {
try {
int missing = 4 - StringUtils.countMatches(id, "-");
// Lenient - add missing data
if (missing > 0) {
id += StringUtils.repeat("-0", missing);
}
return UUID.fromString(id);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Cannot construct profile [" + id + ", " + name + "]", e);
}
}
/**
* Construct a new game profile with the given properties.
* @param uuid - the UUID of the player.
* @param name - the name of the player.
*/
public WrappedGameProfile(UUID uuid, String name) {
super(GameProfile.class);
if (CREATE_STRING_STRING != null) {
setHandle(CREATE_STRING_STRING.invoke(uuid.toString(), name));
} else {
setHandle(new GameProfile(uuid, name));
}
}
/**
@ -39,7 +87,9 @@ public class WrappedGameProfile extends AbstractWrapper {
* @return The UUID of the player, or NULL if not computed.
*/
public String getId() {
return getProfile().getId();
if (GET_UUID_STRING == null)
return (String) GET_UUID_STRING.get(handle);
return getProfile().getId() != null ? getProfile().getId().toString() : null;
}
/**

View File

@ -288,7 +288,7 @@ public class WrappedServerPing extends AbstractWrapper {
for (Player player : players) {
GameProfile profile = (GameProfile) ENTITY_HUMAN_PROFILE.get(BukkitUnwrapper.getInstance().unwrapItem(player));
profiles.add(new WrappedGameProfile(profile.getId(), profile.getName()));
profiles.add(WrappedGameProfile.fromHandle(profile));
}
setPlayers(profiles);
}

View File

@ -6,6 +6,8 @@ import java.lang.reflect.Method;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
@ -14,9 +16,62 @@ import com.comphenix.protocol.wrappers.nbt.NbtList;
import com.comphenix.protocol.wrappers.nbt.NbtWrapper;
public class NbtBinarySerializer {
private static final Class<?> NBT_BASE_CLASS = MinecraftReflection.getNBTBaseClass();
private interface LoadMethod {
/**
* Load an NBT compound from a given stream.
* @param input - the input stream.
* @return The loaded NBT compound.
*/
public abstract Object loadNbt(DataInput input);
}
/**
* Load an NBT compound from the NBTBase static method pre-1.7.2.
*/
private static class LoadMethodNbtClass implements LoadMethod {
private MethodAccessor accessor = getNbtLoadMethod(DataInput.class);
@Override
public Object loadNbt(DataInput input) {
return accessor.invoke(null, input);
}
}
/**
* Load an NBT compound from the NBTCompressedStreamTools static method in 1.7.2 - 1.7.5
*/
private static class LoadMethodWorldUpdate implements LoadMethod {
private MethodAccessor accessor = getNbtLoadMethod(DataInput.class, int.class);
@Override
public Object loadNbt(DataInput input) {
return accessor.invoke(null, input, 0);
}
}
/**
* Load an NBT compound from the NBTCompressedStreamTools static method in 1.7.8
*/
private static class LoadMethodSkinUpdate implements LoadMethod {
private Class<?> readLimitClass = MinecraftReflection.getNBTReadLimiterClass();
private Object readLimiter = FuzzyReflection.fromClass(readLimitClass).getSingleton();
private MethodAccessor accessor = getNbtLoadMethod(DataInput.class, int.class, readLimitClass);
@Override
public Object loadNbt(DataInput input) {
return accessor.invoke(null, input, 0, readLimiter);
}
}
// Used to read and write NBT
private static Method methodWrite;
private static Method methodLoad;
/**
* Method selected for loading NBT compounds.
*/
private static LoadMethod loadMethod;
/**
* Retrieve a default instance of the NBT binary serializer.
@ -51,32 +106,36 @@ public class NbtBinarySerializer {
* @return An NBT tag.
*/
public <TType> NbtWrapper<TType> deserialize(DataInput source) {
if (methodLoad == null) {
Class<?> base = MinecraftReflection.getNBTBaseClass();
Class<?>[] params = MinecraftReflection.isUsingNetty() ?
new Class<?>[] { DataInput.class, int.class } :
new Class<?>[] { DataInput.class };
LoadMethod method = loadMethod;
if (loadMethod == null) {
if (MinecraftReflection.isUsingNetty()) {
try {
method = new LoadMethodWorldUpdate();
} catch (IllegalArgumentException e) {
// Cannot find that method - must be in 1.7.8
method = new LoadMethodSkinUpdate();
}
} else {
method = new LoadMethodNbtClass();
}
// Use the base class
methodLoad = getUtilityClass().getMethodByParameters("load", base, params);
methodLoad.setAccessible(true);
// Save the selected method
loadMethod = method;
}
try {
Object result = null;
// Invoke the correct utility method
if (MinecraftReflection.isUsingNetty())
result = methodLoad.invoke(null, source, 0);
else
result = methodLoad.invoke(null, source);
return NbtFactory.fromNMS(result, null);
return NbtFactory.fromNMS(method.loadNbt(source), null);
} catch (Exception e) {
throw new FieldAccessException("Unable to read NBT from " + source, e);
}
}
private FuzzyReflection getUtilityClass() {
private static MethodAccessor getNbtLoadMethod(Class<?>... parameters) {
return Accessors.getMethodAccessor(getUtilityClass().getMethodByParameters("load", NBT_BASE_CLASS, parameters), true);
}
private static FuzzyReflection getUtilityClass() {
if (MinecraftReflection.isUsingNetty()) {
return FuzzyReflection.fromClass(MinecraftReflection.getNbtCompressedStreamToolsClass(), true);
} else {

View File

@ -4,12 +4,12 @@ import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import net.minecraft.server.v1_7_R1.Block;
import net.minecraft.server.v1_7_R1.Item;
import net.minecraft.server.v1_7_R1.StatisticList;
import net.minecraft.server.v1_7_R3.Block;
import net.minecraft.server.v1_7_R3.Item;
import net.minecraft.server.v1_7_R3.StatisticList;
// Will have to be updated for every version though
import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemFactory;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@ -69,6 +69,6 @@ public class BukkitInitialization {
*/
public static void initializePackage() {
// Initialize reflection
MinecraftReflection.setMinecraftPackage("net.minecraft.server.v1_7_R1", "org.bukkit.craftbukkit.v1_7_R1");
MinecraftReflection.setMinecraftPackage("net.minecraft.server.v1_7_R3", "org.bukkit.craftbukkit.v1_7_R3");
}
}

View File

@ -22,15 +22,15 @@ import java.lang.reflect.Array;
import java.util.List;
import java.util.UUID;
import net.minecraft.server.v1_7_R1.AttributeModifier;
import net.minecraft.server.v1_7_R1.AttributeSnapshot;
import net.minecraft.server.v1_7_R1.PacketPlayOutUpdateAttributes;
import net.minecraft.server.v1_7_R3.AttributeModifier;
import net.minecraft.server.v1_7_R3.AttributeSnapshot;
import net.minecraft.server.v1_7_R3.PacketPlayOutUpdateAttributes;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
// Will have to be updated for every version though
import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemFactory;
import org.bukkit.ChatColor;
import org.bukkit.Material;
@ -332,7 +332,7 @@ public class PacketContainerTest {
@Test
public void testGameProfiles() {
PacketContainer spawnEntity = new PacketContainer(PacketType.Play.Server.NAMED_ENTITY_SPAWN);
WrappedGameProfile profile = new WrappedGameProfile("id", "name");
WrappedGameProfile profile = new WrappedGameProfile("d7047a08-3150-4aa8-a2f2-7c1e2b17e298", "name");
spawnEntity.getGameProfiles().write(0, profile);
assertEquals(profile, spawnEntity.getGameProfiles().read(0));

View File

@ -3,14 +3,14 @@ package com.comphenix.protocol.utility;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import net.minecraft.server.v1_7_R1.ChatComponentText;
import net.minecraft.server.v1_7_R1.ChatSerializer;
import net.minecraft.server.v1_7_R1.ChunkCoordIntPair;
import net.minecraft.server.v1_7_R1.IChatBaseComponent;
import net.minecraft.server.v1_7_R1.NBTCompressedStreamTools;
import net.minecraft.server.v1_7_R1.ServerPing;
import net.minecraft.server.v1_7_R1.ServerPingPlayerSample;
import net.minecraft.server.v1_7_R1.ServerPingServerData;
import net.minecraft.server.v1_7_R3.ChatComponentText;
import net.minecraft.server.v1_7_R3.ChatSerializer;
import net.minecraft.server.v1_7_R3.ChunkCoordIntPair;
import net.minecraft.server.v1_7_R3.IChatBaseComponent;
import net.minecraft.server.v1_7_R3.NBTCompressedStreamTools;
import net.minecraft.server.v1_7_R3.ServerPing;
import net.minecraft.server.v1_7_R3.ServerPingPlayerSample;
import net.minecraft.server.v1_7_R3.ServerPingServerData;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;

View File

@ -8,10 +8,10 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import net.minecraft.server.v1_7_R1.IntHashMap;
import net.minecraft.server.v1_7_R3.IntHashMap;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemFactory;
import org.bukkit.inventory.ItemStack;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -15,15 +15,15 @@ public class ChunkCoordIntPairTest {
@Test
public void test() {
net.minecraft.server.v1_7_R1.ChunkCoordIntPair pair = new net.minecraft.server.v1_7_R1.ChunkCoordIntPair(1, 2);
net.minecraft.server.v1_7_R3.ChunkCoordIntPair pair = new net.minecraft.server.v1_7_R3.ChunkCoordIntPair(1, 2);
ChunkCoordIntPair specific = ChunkCoordIntPair.getConverter().getSpecific(pair);
assertEquals(1, specific.getChunkX());
assertEquals(2, specific.getChunkZ());
net.minecraft.server.v1_7_R1.ChunkCoordIntPair roundtrip =
(net.minecraft.server.v1_7_R1.ChunkCoordIntPair) ChunkCoordIntPair.getConverter().
getGeneric(net.minecraft.server.v1_7_R1.ChunkCoordIntPair.class, specific);
net.minecraft.server.v1_7_R3.ChunkCoordIntPair roundtrip =
(net.minecraft.server.v1_7_R3.ChunkCoordIntPair) ChunkCoordIntPair.getConverter().
getGeneric(net.minecraft.server.v1_7_R3.ChunkCoordIntPair.class, specific);
assertEquals(1, roundtrip.x);
assertEquals(2, roundtrip.z);

View File

@ -16,7 +16,7 @@ public class CloningTest {
@Test
public void cloneGameProfile() {
WrappedGameProfile profile = new WrappedGameProfile("id", "name");
WrappedGameProfile profile = new WrappedGameProfile("8817d9ec-72e6-4abe-a496-cda667c3efe1", "name");
WrappedGameProfile copy = WrappedGameProfile.fromHandle(
AggregateCloner.DEFAULT.clone(profile.getHandle())
);

View File

@ -2,12 +2,12 @@ package com.comphenix.protocol.wrappers;
import static org.junit.Assert.*;
import net.minecraft.server.v1_7_R1.EnumChatVisibility;
import net.minecraft.server.v1_7_R1.EnumClientCommand;
import net.minecraft.server.v1_7_R1.EnumDifficulty;
import net.minecraft.server.v1_7_R1.EnumEntityUseAction;
import net.minecraft.server.v1_7_R1.EnumGamemode;
import net.minecraft.server.v1_7_R1.EnumProtocol;
import net.minecraft.server.v1_7_R3.EnumChatVisibility;
import net.minecraft.server.v1_7_R3.EnumClientCommand;
import net.minecraft.server.v1_7_R3.EnumDifficulty;
import net.minecraft.server.v1_7_R3.EnumEntityUseAction;
import net.minecraft.server.v1_7_R3.EnumGamemode;
import net.minecraft.server.v1_7_R3.EnumProtocol;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -4,9 +4,9 @@ import static org.junit.Assert.*;
import java.util.List;
import net.minecraft.server.v1_7_R1.AttributeModifier;
import net.minecraft.server.v1_7_R1.AttributeSnapshot;
import net.minecraft.server.v1_7_R1.PacketPlayOutUpdateAttributes;
import net.minecraft.server.v1_7_R3.AttributeModifier;
import net.minecraft.server.v1_7_R3.AttributeSnapshot;
import net.minecraft.server.v1_7_R3.PacketPlayOutUpdateAttributes;
import org.junit.Before;
import org.junit.BeforeClass;

View File

@ -3,7 +3,7 @@ package com.comphenix.protocol.wrappers;
import static org.junit.Assert.*;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemFactory;
import org.bukkit.inventory.ItemStack;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -27,7 +27,7 @@ import java.io.DataOutput;
import java.io.DataOutputStream;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemFactory;
import org.bukkit.inventory.ItemStack;
import org.junit.BeforeClass;
import org.junit.Test;