Compare commits

...
24 Commits
Author SHA1 Message Date
Dan Mulloy 31a0c9b10c Update version in plugin.yml 2016-09-18 15:27:49 -04:00
Dan Mulloy e4d95e09cf Update version to 4.1.0 for release 2016-09-18 15:27:01 -04:00
Dan Mulloy d173be0db5 Actually use UpdateSign when it exists 2016-09-18 14:42:40 -04:00
Dan Mulloy 051989a396 Merge pull request #265 from faradize/master
Added the ability of reading/writing lists of NBT classes!
2016-09-11 17:00:25 -04:00
Dan Mulloy 795c77e943 Fix minor example issue
Closes aadnk#113
2016-09-11 16:56:05 -04:00
Dan Mulloy 1de6bbc9dc Make sure we use the update sign packet when it exists
Should fix #263
2016-09-11 16:52:45 -04:00
verbuchselt 474c74b1f9 Added the ability of reading/writing lists of NBT classes!
Works perfectly with MapChunk-Packet's TileEntity-Data!
2016-09-07 11:37:30 +02:00
Dan Mulloy caaa57c915 Add basic TileEntityTest 2016-08-27 13:08:22 -04:00
Dan Mulloy 588f736348 Fix missing TileEntity write methods
Fixes #249
2016-08-27 13:07:49 -04:00
Dan Mulloy 828302150a Deal with plugins that create proxy player objects
As I was writing this I realized it sounded like Bungee support, but it
isn't
2016-08-13 13:19:13 -04:00
Dan Mulloy 144723af18 Ensure we're always using leniency with components
Fixes #252
2016-08-06 14:40:49 -04:00
Dan Mulloy 087913ab3a Standardize equals and hashCode for wrappers 2016-08-06 13:19:14 -04:00
Dan Mulloy 64942cbb5e Make much of ProtocolLib locale independent
Fixes #242
2016-07-25 14:09:23 -04:00
Dan Mulloy 97430a8e42 Add renamed OPEN_SIGN_ENTITY packet for backwards compat
Fixes #243
2016-07-25 13:47:56 -04:00
Dan Mulloy 1bdd200794 Try to fix a weird null case 2016-07-16 16:03:01 -04:00
Dan Mulloy 679035c3bf Fix an error with wire packets
Addresses #224
2016-07-16 15:46:23 -04:00
Dan Mulloy bd4352f041 Fix cloning data watchers
Addresses #224
2016-07-12 16:15:46 -04:00
Dan Mulloy 05ffeb8e7f Restore compatibility with 1.8
1.8.8 is still one of the most popular server versions. As a result,
many servers were not receiving important fixes and new APIs.

I was able to accomplish this with minimal bloat, making it worth it.

Upon release, 4.1.0 will become the recommended version for 1.8 thru the
current Spigot build.
2016-07-12 13:37:55 -04:00
Dan Mulloy 6c982a83f0 Cache null classes
This should prevent multiple unnecessary calls to the expensive
loadClass method

Fixes #236
2016-07-09 15:32:35 -04:00
Dan Mulloy 9860fbaec6 Fix API versioning 2016-07-07 12:19:10 -04:00
Dan Mulloy 26c29d9c85 Add conversion methods to WirePacket 2016-07-06 15:47:43 -04:00
Dan Mulloy 242b25eb1e Wrap all serializable objects in data watchers 2016-07-06 15:47:24 -04:00
Dan Mulloy c13b8d4fc6 Add modifiers for UUIDs and Directions 2016-07-06 15:46:42 -04:00
Dan Mulloy 12e9c08a1a Update version to 4.1.0-SNAPSHOT 2016-07-06 15:44:41 -04:00
36 changed files with 1327 additions and 324 deletions

No files matched your search

+1 -1
View File
@@ -47,7 +47,7 @@ You can also add ProtocolLib as a Maven dependency:
<url>http://repo.dmulloy2.net/content/groups/public/</url>
</repository>
...
</repository>
</repositories>
<dependencies>
<dependency>
+1 -1
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib-API</artifactId>
<name>ProtocolLib-API</name>
<version>4.0-SNAPSHOT</version>
<version>4.1.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
@@ -31,7 +32,7 @@ import com.google.common.util.concurrent.Futures;
* Note that vanilla Minecraft reuses packet IDs per protocol (ping, game, login) and IDs are subject to change, so they are not reliable.
* @author Kristian
*/
public class PacketType implements Serializable, Comparable<PacketType> {
public class PacketType implements Serializable, Cloneable, Comparable<PacketType> {
// Increment whenever the type changes
private static final long serialVersionUID = 1L;
@@ -182,31 +183,61 @@ public class PacketType implements Serializable, Comparable<PacketType> {
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x4B, 0x20, "UpdateAttributes");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x4C, 0x1D, "EntityEffect");
/**
* @deprecated Replaced by {@link WINDOW_DATA}
*/
@Deprecated
public static final PacketType CRAFT_PROGRESS_BAR = WINDOW_DATA;
// ---- Removed in 1.9
/**
* @deprecated Replaced by {@link REL_ENTITY_MOVE_LOOK}
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType ENTITY_MOVE_LOOK = REL_ENTITY_MOVE_LOOK;
public static final PacketType MAP_CHUNK_BULK = new PacketType(PROTOCOL, SENDER, 255, 255, "MapChunkBulk").deprecatedIn(MinecraftVersion.COMBAT_UPDATE);
/**
* @deprecated Replaced by {@link STATISTIC}
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType STATISTICS = STATISTIC;
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 254, 254, "SetCompression").deprecatedIn(MinecraftVersion.COMBAT_UPDATE);
/**
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType UPDATE_ENTITY_NBT = new PacketType(PROTOCOL, SENDER, 253, 253, "UpdateEntityNBT").deprecatedIn(MinecraftVersion.COMBAT_UPDATE);
// ----- Renamed packets
/**
* @deprecated Renamed to {@link WINDOW_DATA}
*/
@Deprecated
public static final PacketType CRAFT_PROGRESS_BAR = WINDOW_DATA.deprecated();
/**
* @deprecated Renamed to {@link REL_ENTITY_MOVE_LOOK}
*/
@Deprecated
public static final PacketType ENTITY_MOVE_LOOK = REL_ENTITY_MOVE_LOOK.deprecated();
/**
* @deprecated Renamed to {@link STATISTIC}
*/
@Deprecated
public static final PacketType STATISTICS = STATISTIC.deprecated();
/**
* @deprecated Renamed to {@link OPEN_SIGN_EDITOR}
*/
@Deprecated
public static final PacketType OPEN_SIGN_ENTITY = OPEN_SIGN_EDITOR.deprecated();
// ----- Replaced in 1.9.4
/**
* @deprecated Replaced by {@link TILE_ENTITY_DATA}
*/
@Deprecated
public static final PacketType UPDATE_SIGN = TILE_ENTITY_DATA;
public static final PacketType UPDATE_SIGN = MinecraftReflection.signUpdateExists() ? new PacketType(PROTOCOL, SENDER, 252, 252, "UpdateSign") :
TILE_ENTITY_DATA.deprecated();
// The instance must
private final static Server INSTANCE = new Server();
// Prevent accidental construction
@@ -297,7 +328,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @deprecated Replaced by {@link SERVER_INFO}
*/
@Deprecated
public static final PacketType OUT_SERVER_INFO = SERVER_INFO;
public static final PacketType OUT_SERVER_INFO = SERVER_INFO.deprecated();
private final static Server INSTANCE = new Server();
@@ -512,7 +543,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
}
public String getPacketName() {
return WordUtils.capitalize(name().toLowerCase());
return WordUtils.capitalize(name().toLowerCase(Locale.ENGLISH));
}
}
@@ -551,7 +582,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
/**
* Protocol version of all the current IDs.
*/
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.BOUNTIFUL_UPDATE;
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.FROSTBURN_UPDATE;
private final Protocol protocol;
private final Sender sender;
@@ -562,6 +593,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
private boolean forceAsync;
private boolean dynamic;
private boolean deprecated;
/**
* Retrieve the current packet/legacy lookup.
@@ -1043,6 +1075,24 @@ public class PacketType implements Serializable, Comparable<PacketType> {
return forceAsync;
}
private PacketType deprecatedIn(MinecraftVersion version) {
try {
return MinecraftVersion.getCurrentVersion().isAtLeast(version) ? deprecated() : this;
} catch (Throwable ex) {
return deprecated();
}
}
private PacketType deprecated() {
PacketType ret = clone();
ret.deprecated = true;
return ret;
}
public boolean isDeprecated() {
return deprecated;
}
@Override
public int hashCode() {
return Objects.hashCode(protocol, sender, currentId, legacyId);
@@ -1078,8 +1128,17 @@ public class PacketType implements Serializable, Comparable<PacketType> {
Class<?> clazz = getPacketClass();
if (clazz == null)
return "UNREGISTERED[" + protocol + ", " + sender + ", " + currentId + ", legacy: " + legacyId + ", classNames: " + Arrays.toString(classNames) + "]";
return name() + "[" + protocol + ", " + sender + ", " + currentId + ", legacy: " + legacyId + ", classNames: " + Arrays.toString(classNames) + " (unregistered)]";
else
return clazz.getSimpleName() + "[" + currentId + ", legacy: " + legacyId + "]";
}
@Override
public PacketType clone() {
try {
return (PacketType) super.clone();
} catch (CloneNotSupportedException ex) {
throw new Error("This shouldn't happen", ex);
}
}
}
@@ -20,6 +20,7 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
@@ -464,7 +465,7 @@ public class ProtocolConfig {
PlayerInjectHooks hook = getDefaultMethod();
if (text != null)
hook = PlayerInjectHooks.valueOf(text.toUpperCase().replace(" ", "_"));
hook = PlayerInjectHooks.valueOf(text.toUpperCase(Locale.ENGLISH).replace(" ", "_"));
return hook;
}
@@ -34,7 +34,7 @@ public class ProtocolLibrary {
/**
* The minimum version ProtocolLib has been tested with.
*/
public static final String MINIMUM_MINECRAFT_VERSION = "1.9";
public static final String MINIMUM_MINECRAFT_VERSION = "1.8";
/**
* The maximum version ProtocolLib has been tested with.
@@ -33,6 +33,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nonnull;
@@ -77,6 +78,7 @@ import com.comphenix.protocol.wrappers.EnumWrappers.ChatVisibility;
import com.comphenix.protocol.wrappers.EnumWrappers.ClientCommand;
import com.comphenix.protocol.wrappers.EnumWrappers.CombatEventType;
import com.comphenix.protocol.wrappers.EnumWrappers.Difficulty;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.EnumConverter;
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
@@ -332,7 +334,15 @@ public class PacketContainer implements Serializable {
public StructureModifier<String> getStrings() {
return structureModifier.withType(String.class);
}
/**
* Retrieves a read/write structure for every UUID field.
* @return A modifier for every UUID field.
*/
public StructureModifier<UUID> getUUIDs() {
return structureModifier.withType(UUID.class);
}
/**
* Retrieves a read/write structure for every String array field.
* @return A modifier for every String array field.
@@ -368,7 +378,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for ItemStack.
* <p>
* This modifier will automatically marshall between the Bukkit ItemStack and the
* This modifier will automatically marshal between the Bukkit ItemStack and the
* internal Minecraft ItemStack.
* @return A modifier for ItemStack fields.
*/
@@ -381,7 +391,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for arrays of ItemStacks.
* <p>
* This modifier will automatically marshall between the Bukkit ItemStack and the
* This modifier will automatically marshal between the Bukkit ItemStack and the
* internal Minecraft ItemStack.
* @return A modifier for ItemStack array fields.
*/
@@ -410,7 +420,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for the world type enum.
* <p>
* This modifier will automatically marshall between the Bukkit world type and the
* This modifier will automatically marshal between the Bukkit world type and the
* internal Minecraft world type.
* @return A modifier for world type fields.
*/
@@ -510,6 +520,21 @@ public class PacketContainer implements Serializable {
BukkitConverters.getNbtConverter());
}
/**
* Retrieves a read/write structure for lists of NBT classes.
* @return A modifier for lists of NBT classes.
*/
public StructureModifier<List<NbtBase<?>>> getListNbtModifier() {
// Convert to and from the ProtocolLib wrapper
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(
MinecraftReflection.getNBTBaseClass(),
BukkitConverters.getNbtConverter()
)
);
}
/**
* Retrieves a read/write structure for Vectors.
* @return A modifier for Vectors.
@@ -524,7 +549,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for collections of attribute snapshots.
* <p>
* This modifier will automatically marshall between the visible ProtocolLib WrappedAttribute and the
* This modifier will automatically marshal between the visible ProtocolLib WrappedAttribute and the
* internal Minecraft AttributeSnapshot.
* @return A modifier for AttributeSnapshot collection fields.
*/
@@ -541,7 +566,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for collections of chunk positions.
* <p>
* This modifier will automatically marshall between the visible ProtocolLib ChunkPosition and the
* This modifier will automatically marshal between the visible ProtocolLib ChunkPosition and the
* internal Minecraft ChunkPosition.
*
* @return A modifier for ChunkPosition list fields.
@@ -559,7 +584,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for collections of chunk positions.
* <p>
* This modifier will automatically marshall between the visible ProtocolLib BlockPosition and the
* This modifier will automatically marshal between the visible ProtocolLib BlockPosition and the
* internal Minecraft BlockPosition.
*
* @return A modifier for ChunkPosition list fields.
@@ -577,7 +602,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for collections of watchable objects.
* <p>
* This modifier will automatically marshall between the visible WrappedWatchableObject and the
* This modifier will automatically marshal between the visible WrappedWatchableObject and the
* internal Minecraft WatchableObject.
* @return A modifier for watchable object list fields.
*/
@@ -594,7 +619,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for block fields.
* <p>
* This modifier will automatically marshall between Material and the
* This modifier will automatically marshal between Material and the
* internal Minecraft Block.
* @return A modifier for GameProfile fields.
*/
@@ -607,7 +632,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for game profiles in Minecraft 1.7.2.
* <p>
* This modifier will automatically marshall between WrappedGameProfile and the
* This modifier will automatically marshal between WrappedGameProfile and the
* internal Minecraft GameProfile.
* @return A modifier for GameProfile fields.
*/
@@ -620,7 +645,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for BlockData in Minecraft 1.8.
* <p>
* This modifier will automatically marshall between WrappedBlockData and the
* This modifier will automatically marshal between WrappedBlockData and the
* internal Minecraft IBlockData.
* @return A modifier for BlockData fields.
*/
@@ -633,7 +658,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for MultiBlockChangeInfo arrays in Minecraft 1.8.
* <p>
* This modifier will automatically marshall between MultiBlockChangeInfo and the
* This modifier will automatically marshal between MultiBlockChangeInfo and the
* internal Minecraft MultiBlockChangeInfo.
* @return A modifier for BlockData fields.
*/
@@ -648,7 +673,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for chat components in Minecraft 1.7.2.
* <p>
* This modifier will automatically marshall between WrappedChatComponent and the
* This modifier will automatically marshal between WrappedChatComponent and the
* internal Minecraft IChatBaseComponent.
* @return A modifier for ChatComponent fields.
*/
@@ -887,7 +912,7 @@ public class PacketContainer implements Serializable {
}
/**
* Retrive a read/write structure for the ItemSlot enum in 1.9.
* Retrieve a read/write structure for the ItemSlot enum in 1.9.
* @return A modifier for ItemSlot enum fields.
*/
public StructureModifier<ItemSlot> getItemSlots() {
@@ -896,7 +921,7 @@ public class PacketContainer implements Serializable {
}
/**
* Retrive a read/write structure for the Hand enum in 1.9.
* Retrieve a read/write structure for the Hand enum in 1.9.
* @return A modifier for Hand enum fields.
*/
public StructureModifier<Hand> getHands() {
@@ -904,6 +929,15 @@ public class PacketContainer implements Serializable {
EnumWrappers.getHandClass(), EnumWrappers.getHandConverter());
}
/**
* Retrieve a read/write structure for the Direction enum in 1.10.
* @return A modifier for Direction enum fields.
*/
public StructureModifier<Direction> getDirections() {
return structureModifier.<Direction>withType(
EnumWrappers.getDirectionClass(), EnumWrappers.getDirectionConverter());
}
/**
* Retrieve a read/write structure for an enum. This allows for the use of
* user-created enums that may not exist in ProtocolLib. The specific (user
@@ -1319,8 +1353,7 @@ public class PacketContainer implements Serializable {
private static class ComponentArrayConverter implements EquivalentConverter<WrappedChatComponent[]> {
private static final EquivalentConverter<WrappedChatComponent[]> DELEGATE;
static {
Class<?> packetClass = PacketType.Play.Server.UPDATE_SIGN.getPacketClass();
if (packetClass.getName().contains("Sign")) {
if (!PacketType.Play.Server.UPDATE_SIGN.isDeprecated()) {
DELEGATE = new LegacyComponentConverter();
} else {
DELEGATE = new NBTComponentConverter();
@@ -24,6 +24,9 @@ import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
@@ -31,6 +34,7 @@ import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.PacketConstructor.Unwrapper;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.primitives.Primitives;
/**
@@ -182,20 +186,55 @@ public class BukkitUnwrapper implements Unwrapper {
Report.newBuilder(REPORT_SECURITY_LIMITATION).error(e).callerParam(type)
);
} catch (NoSuchMethodException e) {
// Maybe it's a proxy?
Unwrapper proxyUnwrapper = getProxyUnwrapper(type);
if (proxyUnwrapper != null)
return proxyUnwrapper;
// Try getting the field unwrapper too
Unwrapper fieldUnwrapper = getFieldUnwrapper(type);
if (fieldUnwrapper != null)
return fieldUnwrapper;
else
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_FIND_UNWRAP_METHOD).error(e).callerParam(type));
}
// Default method
return null;
}
// Players should /always/ be able to be unwrapped
// We should only get here if the 'Player' is a proxy
private Unwrapper getProxyUnwrapper(final Class<?> type) {
try {
if (Player.class.isAssignableFrom(type)) {
final Method getHandle = MinecraftReflection.getCraftPlayerClass().getMethod("getHandle");
Unwrapper unwrapper = new Unwrapper() {
@Override
public Object unwrapItem(Object wrapped) {
try {
return getHandle.invoke(((Player) wrapped).getPlayer());
} catch (Throwable ex) {
try {
return getHandle.invoke(Bukkit.getPlayer(((Player) wrapped).getUniqueId()));
} catch (ReflectiveOperationException ex1) {
throw new RuntimeException("Failed to unwrap proxy " + wrapped, ex);
}
}
}
};
unwrapperCache.put(type, unwrapper);
return unwrapper;
}
} catch (Throwable ignored) {
}
return null;
}
/**
* Retrieve a cached unwrapper using the handle field.
* @param type - a cached field unwrapper.
@@ -16,31 +16,183 @@
*/
package com.comphenix.protocol.injector.netty;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.reflect.Method;
import java.util.Arrays;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
/**
* A packet represented only by its id and bytes.
* @author dmulloy2
*/
public class WirePacket {
private final int id;
private final byte[] bytes;
/**
* Constructs a new WirePacket with a given type and contents
* @param type Type of the packet
* @param bytes Contents of the packet
*/
public WirePacket(PacketType type, byte[] bytes) {
this.id = checkNotNull(type, "type cannot be null").getCurrentId();
this.bytes = bytes;
}
/**
* Constructs a new WirePacket with a given id and contents
* @param id ID of the packet
* @param bytes Contents of the packet
*/
public WirePacket(int id, byte[] bytes) {
this.id = id;
this.bytes = bytes;
}
/**
* Gets this packet's ID
* @return The ID
*/
public int getId() {
return id;
}
/**
* Gets this packet's contents as a byte array
* @return The contents
*/
public byte[] getBytes() {
return bytes;
}
/**
* Writes the id of this packet to a given output
* @param output Output to write to
*/
public void writeId(ByteBuf output) {
int i = id;
writeVarInt(output, id);
}
/**
* Writes the contents of this packet to a given output
* @param output Output to write to
*/
public void writeBytes(ByteBuf output) {
checkNotNull(output, "output cannot be null!");
output.writeBytes(bytes);
}
/**
* Fully writes the ID and contents of this packet to a given output
* @param output Output to write to
*/
public void writeFully(ByteBuf output) {
writeId(output);
writeBytes(output);
}
/**
* Serializes this packet into a byte buffer
* @return The buffer
*/
public ByteBuf serialize() {
ByteBuf buffer = Unpooled.buffer();
writeFully(buffer);
return buffer;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof WirePacket) {
WirePacket that = (WirePacket) obj;
return this.id == that.id &&
Arrays.equals(this.bytes, that.bytes);
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(bytes);
result = prime * result + id;
return result;
}
@Override
public String toString() {
return "WirePacket[id=" + id + ", bytes=" + Arrays.toString(bytes) + "]";
}
private static byte[] getBytes(ByteBuf buffer) {
byte[] array = new byte[buffer.readableBytes()];
buffer.readBytes(array);
return array;
}
/**
* Creates a WirePacket from an existing PacketContainer
* @param packet Existing packet
* @return The resulting WirePacket
*/
public static WirePacket fromPacket(PacketContainer packet) {
checkNotNull(packet, "packet cannot be null!");
int id = packet.getType().getCurrentId();
ByteBuf buffer = PacketContainer.createPacketBuffer();
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
try {
write.invoke(packet.getHandle(), buffer);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to serialize packet contents.", ex);
}
return new WirePacket(id, getBytes(buffer));
}
/**
* Creates a WirePacket from an existing Minecraft packet
* @param packet Existing Minecraft packet
* @return The resulting WirePacket
* @throws IllegalArgumentException If the packet is null or not a Minecraft packet
*/
public static WirePacket fromPacket(Object packet) {
checkNotNull(packet, "packet cannot be null!");
checkArgument(MinecraftReflection.isPacketClass(packet), "packet must be a Minecraft packet");
PacketType type = PacketType.fromClass(packet.getClass());
int id = type.getCurrentId();
ByteBuf buffer = PacketContainer.createPacketBuffer();
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
try {
write.invoke(packet, buffer);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to serialize packet contents.", ex);
}
return new WirePacket(id, getBytes(buffer));
}
public static void writeVarInt(ByteBuf output, int i) {
checkNotNull(output, "output cannot be null!");
while ((i & -128) != 0) {
output.writeByte(i & 127 | 128);
i >>>= 7;
@@ -49,7 +201,22 @@ public class WirePacket {
output.writeByte(i);
}
public void writeBytes(ByteBuf output) {
output.writeBytes(bytes);
public static int readVarInt(ByteBuf input) {
checkNotNull(input, "input cannot be null!");
int i = 0;
int j = 0;
byte b0;
do {
b0 = input.readByte();
i |= (b0 & 127) << j++ * 7;
if (j > 5) {
throw new RuntimeException("VarInt too big");
}
} while ((b0 & 128) == 128);
return i;
}
}
@@ -18,6 +18,7 @@
package com.comphenix.protocol.injector.packet;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -28,6 +29,7 @@ import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.netty.NettyProtocolRegistry;
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -154,11 +156,10 @@ public class PacketRegistry {
*/
@Deprecated
public static Set<Integer> getServerPackets() throws FieldAccessException {
initialize();
if (LEGACY_SERVER_PACKETS == null) {
LEGACY_SERVER_PACKETS = toLegacy(NETTY.getServerPackets());
LEGACY_SERVER_PACKETS = toLegacy(getServerPacketTypes());
}
return LEGACY_SERVER_PACKETS;
}
@@ -168,9 +169,18 @@ public class PacketRegistry {
*/
public static Set<PacketType> getServerPacketTypes() {
initialize();
NETTY.synchronize();
return NETTY.getServerPackets();
Set<PacketType> types = new HashSet<>();
// Filter out unsupported packets
for (PacketType type : NETTY.getServerPackets()) {
if (!type.isDeprecated()) {
types.add(type);
}
}
return types;
}
/**
@@ -182,11 +192,10 @@ public class PacketRegistry {
*/
@Deprecated
public static Set<Integer> getClientPackets() throws FieldAccessException {
initialize();
if (LEGACY_CLIENT_PACKETS == null) {
LEGACY_CLIENT_PACKETS = toLegacy(NETTY.getClientPackets());
LEGACY_CLIENT_PACKETS = toLegacy(getClientPacketTypes());
}
return LEGACY_CLIENT_PACKETS;
}
@@ -196,9 +205,18 @@ public class PacketRegistry {
*/
public static Set<PacketType> getClientPacketTypes() {
initialize();
NETTY.synchronize();
return NETTY.getClientPackets();
Set<PacketType> types = new HashSet<>();
// Filter out unsupported packets
for (PacketType type : NETTY.getClientPackets()) {
if (!type.isDeprecated()) {
types.add(type);
}
}
return types;
}
/**
@@ -269,7 +287,24 @@ public class PacketRegistry {
*/
public static Class getPacketClassFromType(PacketType type, boolean forceVanilla) {
initialize();
return NETTY.getPacketTypeLookup().get(type);
// Try the lookup first
Class<?> clazz = NETTY.getPacketTypeLookup().get(type);
if (clazz != null) {
return clazz;
}
// Then try looking up the class names
for (String name : type.getClassNames()) {
try {
clazz = MinecraftReflection.getMinecraftClass(name);
break;
} catch (Exception ex) {
}
}
// TODO Cache the result?
return clazz;
}
/**
@@ -52,7 +52,7 @@ public class ObjectEnum<T> implements Iterable<T> {
@SuppressWarnings("unchecked")
protected void registerAll(Class<T> fieldType) {
try {
// Register every int field
// Register every non-deprecated field
for (Field entry : this.getClass().getFields()) {
if (Modifier.isStatic(entry.getModifiers()) && fieldType.isAssignableFrom(entry.getType())) {
T value = (T) entry.get(null);
@@ -63,7 +63,6 @@ public class ObjectEnum<T> implements Iterable<T> {
registerMember(value, entry.getName());
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
@@ -62,7 +62,7 @@ class CachedPackage {
public Class<?> getPackageClass(String className) {
try {
Class<?> result = cache.get(Preconditions.checkNotNull(className, "className cannot be NULL"));
// Concurrency is not a problem - we don't care if we look up a class twice
if (result == null) {
// Look up the class dynamically
@@ -75,10 +75,11 @@ class CachedPackage {
return result;
} catch (ClassNotFoundException e) {
setPackageClass(className, null);
throw new RuntimeException("Cannot find class " + combine(packageName, className), e);
}
}
/**
* Correctly combine a package name and the child class we're looking for.
* @param packageName - name of the package, or an empty string for the default package.
@@ -47,6 +47,7 @@ public class MinecraftFields {
* @return The player connection.
*/
public static Object getPlayerConnection(Player player) {
Preconditions.checkNotNull(player, "player cannot be null!");
return getPlayerConnection(BukkitUnwrapper.getInstance().unwrapItem(player));
}
@@ -152,6 +152,7 @@ public class MinecraftReflection {
// Whether or not we are using netty
private static Boolean cachedNetty;
private static Boolean cachedWatcherObject;
private MinecraftReflection() {
// No need to make this constructable.
@@ -408,14 +409,31 @@ public class MinecraftReflection {
return javaName.startsWith(MINECRAFT_PREFIX_PACKAGE) && javaName.endsWith(className);
}
/**
* Determine if a given Object is compatible with a given Class. That is,
* whether or not the Object is an instance of that Class or one of its
* subclasses. If either is null, false is returned.
*
* @param clazz Class to test for, may be null
* @param object the Object to test, may be null
* @return True if it is, false if not
* @see Class#isAssignableFrom(Class)
*/
public static boolean is(Class<?> clazz, Object object) {
if (clazz == null || object == null) {
return false;
}
return clazz.isAssignableFrom(object.getClass());
}
/**
* Determine if a given object is a ChunkPosition.
* @param obj - the object to test.
* @return TRUE if it can, FALSE otherwise.
*/
public static boolean isChunkPosition(Object obj) {
Class<?> chunkPosition = getChunkPositionClass();
return obj != null && chunkPosition != null && chunkPosition.isAssignableFrom(obj.getClass());
return is(getChunkPositionClass(), obj);
}
/**
@@ -424,8 +442,7 @@ public class MinecraftReflection {
* @return TRUE if it can, FALSE otherwise.
*/
public static boolean isBlockPosition(Object obj) {
Class<?> blockPosition = getBlockPositionClass();
return obj != null && blockPosition != null && blockPosition.isAssignableFrom(obj.getClass());
return is(getBlockPositionClass(), obj);
}
/**
@@ -434,7 +451,7 @@ public class MinecraftReflection {
* @return TRUE if it can, FALSE otherwise.
*/
public static boolean isChunkCoordIntPair(Object obj) {
return obj != null && getChunkCoordIntPair().isAssignableFrom(obj.getClass());
return is(getChunkCoordIntPair(), obj);
}
/**
@@ -443,8 +460,7 @@ public class MinecraftReflection {
* @return TRUE if it can, FALSE otherwise.
*/
public static boolean isChunkCoordinates(Object obj) {
Class<?> chunkCoordinates = getChunkCoordinatesClass();
return obj != null && chunkCoordinates != null && chunkCoordinates.isAssignableFrom(obj.getClass());
return is(getChunkCoordinatesClass(), obj);
}
/**
@@ -453,7 +469,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isPacketClass(Object obj) {
return obj != null && getPacketClass().isAssignableFrom(obj.getClass());
return is(getPacketClass(), obj);
}
/**
@@ -462,7 +478,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isLoginHandler(Object obj) {
return obj != null && getNetLoginHandlerClass().isAssignableFrom(obj.getClass());
return is(getNetLoginHandlerClass(), obj);
}
/**
@@ -471,7 +487,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isServerHandler(Object obj) {
return obj != null && getPlayerConnectionClass().isAssignableFrom(obj.getClass());
return is(getPlayerConnectionClass(), obj);
}
/**
@@ -480,7 +496,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isMinecraftEntity(Object obj) {
return obj != null && getEntityClass().isAssignableFrom(obj.getClass());
return is(getEntityClass(), obj);
}
/**
@@ -489,7 +505,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isItemStack(Object value) {
return value != null && getItemStackClass().isAssignableFrom(value.getClass());
return is(getItemStackClass(), value);
}
/**
@@ -498,7 +514,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isCraftPlayer(Object value) {
return value != null && getCraftPlayerClass().isAssignableFrom(value.getClass());
return is(getCraftPlayerClass(), value);
}
/**
@@ -507,7 +523,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isMinecraftPlayer(Object obj) {
return obj != null && getEntityPlayerClass().isAssignableFrom(obj.getClass());
return is(getEntityPlayerClass(), obj);
}
/**
@@ -516,7 +532,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isWatchableObject(Object obj) {
return obj != null && getWatchableObjectClass().isAssignableFrom(obj.getClass());
return is(getWatchableObjectClass(), obj);
}
/**
@@ -525,7 +541,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isDataWatcher(Object obj) {
return obj != null && getDataWatcherClass().isAssignableFrom(obj.getClass());
return is(getDataWatcherClass(), obj);
}
/**
@@ -534,7 +550,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isIntHashMap(Object obj) {
return obj != null && getIntHashMapClass().isAssignableFrom(obj.getClass());
return is(getIntHashMapClass(), obj);
}
/**
@@ -543,7 +559,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isCraftItemStack(Object obj) {
return obj != null && getCraftItemStackClass().isAssignableFrom(obj.getClass());
return is(getCraftItemStackClass(), obj);
}
/**
@@ -1180,24 +1196,7 @@ public class MinecraftReflection {
try {
return getMinecraftClass("ChunkPosition");
} catch (RuntimeException e) {
try {
Class<?> normalChunkGenerator = getCraftBukkitClass("generator.NormalChunkGenerator");
// ChunkPosition a(net.minecraft.server.World world, String string, int i, int i1, int i2) {
FuzzyMethodContract selected = FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.parameterMatches(getMinecraftObjectMatcher(), 0)
.parameterExactType(String.class, 1)
.parameterExactType(int.class, 2)
.parameterExactType(int.class, 3)
.parameterExactType(int.class, 4)
.build();
return setMinecraftClass("ChunkPosition",
FuzzyReflection.fromClass(normalChunkGenerator).getMethod(selected).getReturnType());
} catch (Throwable ex) {
return null;
}
return null;
}
}
@@ -1315,16 +1314,23 @@ public class MinecraftReflection {
}
public static Class<?> getDataWatcherObjectClass() {
// TODO Implement a fallback
return getMinecraftClass("DataWatcherObject");
try {
return getMinecraftClass("DataWatcherObject");
} catch (RuntimeException ex) {
return null;
}
}
public static boolean watcherObjectExists() {
try {
return getDataWatcherObjectClass() != null;
} catch (RuntimeException e) {
return false;
if (cachedWatcherObject == null) {
try {
return cachedWatcherObject = getDataWatcherObjectClass() != null;
} catch (Throwable ex) {
return cachedWatcherObject = false;
}
}
return cachedWatcherObject;
}
public static Class<?> getDataWatcherSerializerClass() {
@@ -1794,8 +1800,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isPlayerInfoData(Object obj) {
Class<?> clazz = getPlayerInfoDataClass();
return clazz != null && obj.getClass().equals(clazz);
return is(getPlayerInfoDataClass(), obj);
}
/**
@@ -1822,6 +1827,14 @@ public class MinecraftReflection {
return getArrayClass(getMultiBlockChangeInfoClass());
}
public static boolean signUpdateExists() {
try {
return getMinecraftClass("PacketPlayOutUpdateSign") != null;
} catch (RuntimeException ex) {
return false;
}
}
/**
* Retrieve a CraftItemStack from a given ItemStack.
* @param bukkitItemStack - the Bukkit ItemStack to convert.
@@ -47,4 +47,26 @@ public abstract class AbstractWrapper {
public Class<?> getHandleType() {
return handleType;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj instanceof AbstractWrapper) {
AbstractWrapper that = (AbstractWrapper) obj;
return this.handle.equals(that.handle);
}
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
@Override
public String toString() {
return getClass().getName() + "[handle=" + handle + "]";
}
}
@@ -124,7 +124,7 @@ public class BukkitConverters {
* @author Kristian
* @param <T> - type that can be converted.
*/
private static abstract class IgnoreNullConverter<TType> implements EquivalentConverter<TType> {
public static abstract class IgnoreNullConverter<TType> implements EquivalentConverter<TType> {
@Override
public final Object getGeneric(Class<?> genericType, TType specific) {
if (specific != null)
@@ -158,20 +158,20 @@ public class BukkitConverters {
@Override
public boolean equals(Object obj) {
// Very short
if (this == obj)
return true;
if (obj == null)
return false;
// See if they're equivalent
if (this == obj) return true;
if (obj instanceof EquivalentConverter) {
@SuppressWarnings("rawtypes")
EquivalentConverter other = (EquivalentConverter) obj;
return Objects.equal(this.getSpecificType(), other.getSpecificType());
EquivalentConverter<?> that = (EquivalentConverter<?>) obj;
return Objects.equal(this.getSpecificType(), that.getSpecificType());
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(this.getSpecificType());
}
}
/**
@@ -194,23 +194,23 @@ public class BukkitConverters {
@Override
public boolean equals(Object obj) {
// More shortcuts
if (obj == this)
return true;
if (obj == null)
return false;
if (obj == this) return true;
// Add another constraint
if (obj instanceof WorldSpecificConverter && super.equals(obj)) {
@SuppressWarnings("rawtypes")
WorldSpecificConverter other = (WorldSpecificConverter) obj;
return Objects.equal(world, other.world);
WorldSpecificConverter<?> that = (WorldSpecificConverter<?>) obj;
return Objects.equal(this.world, that.world);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(this.getSpecificType(), this.world);
}
}
/**
* Retrieve an equivalent converter for a map of generic keys and primitive values.
* @param <T> Key type
@@ -25,7 +25,6 @@ import net.md_5.bungee.chat.ComponentSerializer;
* Note: The BungeeCord Chat API is not included in CraftBukkit.
* @author dmulloy2
*/
public final class ComponentConverter {
private ComponentConverter() {
@@ -0,0 +1,59 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 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;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Method;
/**
* Handles component parsing in 1.8
* @author dmulloy2
*/
public class ComponentParser {
private ComponentParser() {
}
public static Object deserialize(Object gson, Class<?> component, StringReader str) {
try {
com.google.gson.stream.JsonReader reader = new com.google.gson.stream.JsonReader(str);
reader.setLenient(true);
return ((com.google.gson.Gson) gson).getAdapter(component).read(reader);
} catch (IOException ex) {
throw new RuntimeException("Failed to read JSON", ex);
} catch (LinkageError er) {
return deserializeLegacy(gson, component, str);
}
}
// Should only be needed on 1.8.
private static Object deserializeLegacy(Object gson, Class<?> component, StringReader str) {
try {
Class<?> readerClass = Class.forName("org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonReader");
Object reader = readerClass.getConstructor(StringReader.class).newInstance(str);
Method setLenient = readerClass.getMethod("setLenienent", boolean.class);
setLenient.invoke(reader, true);
Method getAdapter = gson.getClass().getMethod("getAdapter", Class.class);
Object adapter = getAdapter.invoke(gson, component);
Method read = adapter.getClass().getMethod("read", readerClass);
return read.invoke(adapter, reader);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to read JSON", ex);
}
}
}
@@ -1,6 +1,7 @@
package com.comphenix.protocol.wrappers;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.bukkit.GameMode;
@@ -226,7 +227,7 @@ public abstract class EnumWrappers {
BY_NAME = new HashMap<String, Particle>();
for (Particle particle : values()) {
BY_NAME.put(particle.getName().toLowerCase(), particle);
BY_NAME.put(particle.getName().toLowerCase(Locale.ENGLISH), particle);
BY_ID.put(particle.getId(), particle);
}
}
@@ -264,7 +265,7 @@ public abstract class EnumWrappers {
}
public static Particle getByName(String name) {
return BY_NAME.get(name.toLowerCase());
return BY_NAME.get(name.toLowerCase(Locale.ENGLISH));
}
public static Particle getById(int id) {
@@ -302,7 +303,7 @@ public abstract class EnumWrappers {
}
public static SoundCategory getByKey(String key) {
return LOOKUP.get(key.toLowerCase());
return LOOKUP.get(key.toLowerCase(Locale.ENGLISH));
}
}
@@ -320,6 +321,15 @@ public abstract class EnumWrappers {
OFF_HAND;
}
public enum Direction {
DOWN(),
UP(),
NORTH(),
SOUTH(),
WEST(),
EAST();
}
private static Class<?> PROTOCOL_CLASS = null;
private static Class<?> CLIENT_COMMAND_CLASS = null;
private static Class<?> CHAT_VISIBILITY_CLASS = null;
@@ -338,6 +348,7 @@ public abstract class EnumWrappers {
private static Class<?> SOUND_CATEGORY_CLASS = null;
private static Class<?> ITEM_SLOT_CLASS = null;
private static Class<?> HAND_CLASS = null;
private static Class<?> DIRECTION_CLASS = null;
private static boolean INITIALIZED = false;
private static Map<Class<?>, EquivalentConverter<?>> FROM_NATIVE = Maps.newHashMap();
@@ -373,6 +384,7 @@ public abstract class EnumWrappers {
SOUND_CATEGORY_CLASS = getEnum(PacketType.Play.Server.CUSTOM_SOUND_EFFECT.getPacketClass(), 0);
ITEM_SLOT_CLASS = getEnum(PacketType.Play.Server.ENTITY_EQUIPMENT.getPacketClass(), 0);
HAND_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 1);
DIRECTION_CLASS = getEnum(PacketType.Play.Client.USE_ITEM.getPacketClass(), 0);
associate(PROTOCOL_CLASS, Protocol.class, getClientCommandConverter());
associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
@@ -392,6 +404,7 @@ public abstract class EnumWrappers {
associate(SOUND_CATEGORY_CLASS, SoundCategory.class, getSoundCategoryConverter());
associate(ITEM_SLOT_CLASS, ItemSlot.class, getItemSlotConverter());
associate(HAND_CLASS, Hand.class, getHandConverter());
associate(DIRECTION_CLASS, Direction.class, getDirectionConverter());
INITIALIZED = true;
}
@@ -515,6 +528,11 @@ public abstract class EnumWrappers {
return HAND_CLASS;
}
public static Class<?> getDirectionClass() {
initialize();
return DIRECTION_CLASS;
}
// Get the converters
public static EquivalentConverter<Protocol> getProtocolConverter() {
return new EnumConverter<Protocol>(Protocol.class);
@@ -588,6 +606,10 @@ public abstract class EnumWrappers {
return new EnumConverter<Hand>(Hand.class);
}
public static EquivalentConverter<Direction> getDirectionConverter() {
return new EnumConverter<Direction>(Direction.class);
}
/**
* Retrieve a generic enum converter for use with StructureModifiers.
* @param enumClass - Enum class
@@ -17,6 +17,7 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import java.util.Locale;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
@@ -70,7 +71,7 @@ public class MinecraftKey {
* @return The resulting key
*/
public static MinecraftKey fromEnum(Enum<?> value) {
return new MinecraftKey(value.name().toLowerCase().replace("_", "."));
return new MinecraftKey(value.name().toLowerCase(Locale.ENGLISH).replace("_", "."));
}
/**
@@ -104,7 +105,7 @@ public class MinecraftKey {
* @return The enum format
*/
public String getEnumFormat() {
return key.toUpperCase().replace(".", "_");
return key.toUpperCase(Locale.ENGLISH).replace(".", "_");
}
private static Constructor<?> constructor = null;
@@ -0,0 +1,129 @@
/**
* (c) 2016 dmulloy2
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BukkitConverters.IgnoreNullConverter;
/**
* @author dmulloy2
*/
public class Vector3F {
protected float x;
protected float y;
protected float z;
public Vector3F() {
this(0, 0, 0);
}
public Vector3F(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public float getX() {
return x;
}
public Vector3F setX(float x) {
this.x = x;
return this;
}
public float getY() {
return y;
}
public Vector3F setY(float y) {
this.y = y;
return this;
}
public float getZ() {
return z;
}
public Vector3F setZ(float z) {
this.z = z;
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Float.floatToIntBits(x);
result = prime * result + Float.floatToIntBits(y);
result = prime * result + Float.floatToIntBits(z);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof Vector3F) {
Vector3F that = (Vector3F) obj;
if (Float.floatToIntBits(x) != Float.floatToIntBits(that.x))
return false;
if (Float.floatToIntBits(y) != Float.floatToIntBits(that.y))
return false;
if (Float.floatToIntBits(z) != Float.floatToIntBits(that.z))
return false;
return true;
}
return false;
}
private static Constructor<?> constructor = null;
private static Class<?> clazz = MinecraftReflection.getMinecraftClass("Vector3f");
public static Class<?> getMinecraftClass() {
return clazz;
}
public static EquivalentConverter<Vector3F> getConverter() {
return new IgnoreNullConverter<Vector3F>() {
@Override
public Class<Vector3F> getSpecificType() {
return Vector3F.class;
}
@Override
protected Object getGenericValue(Class<?> genericType, Vector3F specific) {
if (constructor == null) {
try {
constructor = clazz.getConstructor(float.class, float.class, float.class);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to find constructor for Vector3f", ex);
}
}
try {
return constructor.newInstance(specific.x, specific.y, specific.z);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to create new instance of Vector3f", ex);
}
}
@Override
protected Vector3F getSpecificValue(Object generic) {
StructureModifier<Float> modifier = new StructureModifier<Float>(generic.getClass())
.withTarget(generic).withType(float.class);
float x = modifier.read(0);
float y = modifier.read(1);
float z = modifier.read(2);
return new Vector3F(x, y, z);
}
};
}
}
@@ -162,13 +162,22 @@ public class WrappedBlockData extends AbstractWrapper {
return "WrappedBlockData[handle=" + handle + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getType().hashCode();
result = prime * result + getData();
return result;
}
@Override
public boolean equals(Object o) {
if (o instanceof WrappedBlockData) {
WrappedBlockData that = (WrappedBlockData) o;
return this.getType() == that.getType();
return this.getType() == that.getType() && getData() == that.getData();
}
return false;
}
}
}
@@ -1,10 +1,10 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Method;
import java.util.List;
import java.io.StringReader;
import org.bukkit.ChatColor;
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;
@@ -19,18 +19,35 @@ import com.google.common.base.Preconditions;
public class WrappedChatComponent extends AbstractWrapper {
private static final Class<?> SERIALIZER = MinecraftReflection.getChatSerializerClass();
private static final Class<?> COMPONENT = MinecraftReflection.getIChatBaseComponentClass();
private static final Class<?> GSON_CLASS = MinecraftReflection.getMinecraftGsonClass();
private static Object GSON = null;
private static MethodAccessor DESERIALIZE = null;
private static MethodAccessor SERIALIZE_COMPONENT = null;
private static MethodAccessor DESERIALIZE_COMPONENT = null;
private static MethodAccessor CONSTRUCT_COMPONENT = null;
private static ConstructorAccessor CONSTRUCT_TEXT_COMPONENT = null;
static {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true);
// Retrieve the correct methods
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("serialize", /* a */
String.class, new Class<?>[] { COMPONENT }));
DESERIALIZE_COMPONENT = findDeserialize(fuzzy);
try {
GSON = FieldUtils.readStaticField(fuzzy.getFieldByType("gson", GSON_CLASS), true);
} catch (IllegalAccessException ex) {
throw new RuntimeException("Failed to obtain GSON field", ex);
}
try {
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getMinecraftClass("ChatDeserializer"), true)
.getMethodByParameters("deserialize", Object.class, new Class<?>[] { GSON_CLASS, String.class, Class.class, boolean.class }));
} catch (IllegalArgumentException ex) {
// We'll handle it in the ComponentParser
DESERIALIZE = null;
}
// Get a component from a standard Minecraft message
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(MinecraftReflection.getCraftChatMessage(), "fromString", String.class);
@@ -39,23 +56,17 @@ public class WrappedChatComponent extends AbstractWrapper {
CONSTRUCT_TEXT_COMPONENT = Accessors.getConstructorAccessor(MinecraftReflection.getChatComponentTextClass(), String.class);
}
private static MethodAccessor findDeserialize(FuzzyReflection fuzzy) {
List<Method> methods = fuzzy.getMethodListByParameters(COMPONENT, new Class<?>[] { String.class });
if (methods.isEmpty()) {
throw new IllegalArgumentException("Unable to find deserialize method in " + fuzzy.getSource().getName());
private static Object deserialize(String json) {
// Should be non-null on 1.9 and up
if (DESERIALIZE != null) {
return DESERIALIZE.invoke(null, GSON, json, COMPONENT, true);
}
// Try to find b, we want leniency
for (Method method : methods) {
if (method.getName().equals("b")) {
return Accessors.getMethodAccessor(method);
}
}
// Oh well
return Accessors.getMethodAccessor(methods.get(0));
// Mock leniency behavior in 1.8
StringReader str = new StringReader(json);
return ComponentParser.deserialize(GSON, COMPONENT, str);
}
private transient String cache;
private WrappedChatComponent(Object handle, String cache) {
@@ -79,7 +90,7 @@ public class WrappedChatComponent extends AbstractWrapper {
* @return The chat component wrapper.
*/
public static WrappedChatComponent fromJson(String json) {
return new WrappedChatComponent(DESERIALIZE_COMPONENT.invoke(null, json), json);
return new WrappedChatComponent(deserialize(json), json);
}
/**
@@ -127,7 +138,7 @@ public class WrappedChatComponent extends AbstractWrapper {
* @param obj - the JSON that represents the new component.
*/
public void setJson(String obj) {
this.handle = DESERIALIZE_COMPONENT.invoke(null, obj);
this.handle = deserialize(obj);
this.cache = obj;
}
@@ -138,21 +149,6 @@ public class WrappedChatComponent extends AbstractWrapper {
public WrappedChatComponent deepClone() {
return fromJson(getJson());
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof WrappedChatComponent) {
return ((WrappedChatComponent) obj).handle.equals(handle);
}
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
@Override
public String toString() {
@@ -19,7 +19,6 @@ package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
/**
* Allows access to a chunk coordinate.
@@ -161,24 +160,6 @@ public class WrappedChunkCoordinate extends AbstractWrapper implements Comparabl
return ((Comparable<Object>) handle).compareTo(other.handle);
}
@Override
public boolean equals(Object other) {
if (other instanceof WrappedChunkCoordinate) {
WrappedChunkCoordinate wrapper = (WrappedChunkCoordinate) other;
return Objects.equal(handle, wrapper.handle);
}
// It's tempting to handle the ChunkCoordinate case too, but then
// the equals() method won't be commutative, causing a.equals(b) to
// be different to b.equals(a).
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
@Override
public String toString() {
return String.format("ChunkCoordinate [x: %s, y: %s, z: %s]", getX(), getY(), getZ());
@@ -17,6 +17,7 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@@ -25,6 +26,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Entity;
@@ -43,9 +45,10 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableBiMap;
/**
* Represents a DataWatcher in 1.9
* Represents a DataWatcher in 1.8 thru 1.10
*
* @author dmulloy2
*/
@@ -108,8 +111,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
public WrappedDataWatcher(List<WrappedWatchableObject> objects) {
this();
for (WrappedWatchableObject object : objects) {
setObject(object.getWatcherObject(), object);
if (MinecraftReflection.watcherObjectExists()) {
for (WrappedWatchableObject object : objects) {
setObject(object.getWatcherObject(), object);
}
} else {
for (WrappedWatchableObject object : objects) {
setObject(object.getIndex(), object);
}
}
}
@@ -216,6 +225,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
/**
* @deprecated Renamed to {@link #remove(int)}
*/
@Deprecated
public WrappedWatchableObject removeObject(int index) {
return remove(index);
}
/**
* Removes the item at a given index.
*
@@ -234,7 +251,15 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return True if it does, false if not
*/
public boolean hasIndex(int index) {
return getMap().containsKey(index);
return getObject(index) != null;
}
/**
* Returns a set containing all the registered indexes
* @return The set
*/
public Set<Integer> indexSet() {
return getMap().keySet();
}
/**
@@ -321,46 +346,73 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* Retrieve a watchable object by index.
*
* @param index Index of the object to retrieve.
* @return The watched object.
* @return The watched object or null if it doesn't exist.
*/
public Object getObject(int index) {
return getObject(new WrappedDataWatcherObject(index, null));
return getObject(WrappedDataWatcherObject.fromIndex(index));
}
/**
* Retrieve a watchable object by watcher object.
*
* @param object The watcher object
* @return The watched object
* @return The watched object or null if it doesn't exist.
*/
public Object getObject(WrappedDataWatcherObject object) {
Validate.notNull(object, "Watcher object cannot be null!");
if (GETTER == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType);
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
if (MinecraftReflection.watcherObjectExists()) {
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactType(object.getHandleType())
.returnTypeExact(Object.class)
.build(), "get"));
} else {
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactType(int.class)
.returnTypeExact(MinecraftReflection.getDataWatcherItemClass())
.build()));
}
}
Object value = GETTER.invoke(handle, object.getHandle());
return WrappedWatchableObject.getWrapped(value);
try {
Object value = GETTER.invoke(handle, object.getHandle());
return WrappedWatchableObject.getWrapped(value);
} catch (RuntimeException ex) {
// Nothing exists at this index
return null;
}
}
// ---- Object Setters
/**
* Sets the DataWatcher Item at a given index to a new value.
* Sets the DataWatcher Item at a given index to a new value. In 1.9 and up,
* you cannot register objects without a watcher object.
*
* @param index Index of the object to set
* @param value New value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(WrappedDataWatcherObject, Object)}
* @see {@link #setObject(WrappedDataWatcherObject, Object, boolean)}
* @throws IllegalArgumentException in 1.9 and up if there isn't already an
* object at this index
*/
public void setObject(int index, Object value, boolean update) {
if (MinecraftReflection.watcherObjectExists() && !hasIndex(index)) {
throw new IllegalArgumentException("You cannot register objects without a watcher object!");
}
setObject(WrappedDataWatcherObject.fromIndex(index), value, update);
}
/**
* Shortcut for {@link #setObject(int, Object, boolean)}
*/
public void setObject(int index, Object value) {
Validate.isTrue(hasIndex(index), "You cannot register objects without the watcher object!");
setObject(index, null, value);
setObject(index, value, false);
}
/**
@@ -369,11 +421,39 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @param index Index of the object to set
* @param Serializer Serializer from {@link Serializer#get(Class)}
* @param value New value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(WrappedDataWatcherObject, Object)}
*/
public void setObject(int index, Serializer serializer, Object value, boolean update) {
setObject(new WrappedDataWatcherObject(index, serializer), value, update);
}
/**
* Alias for {@link #setObject(int, Serializer, Object, boolean)}
*/
public void setObject(int index, Serializer serializer, Object value) {
setObject(new WrappedDataWatcherObject(index, serializer), value);
setObject(new WrappedDataWatcherObject(index, serializer), value, false);
}
/**
* Sets the DataWatcher Item at a given index to a new value.
*
* @param index Index of the object to set
* @param value New value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(int, Object, boolean)}
*/
public void setObject(int index, WrappedWatchableObject value, boolean update) {
setObject(index, value.getRawValue(), update);
}
/**
* Alias for {@link #setObject(int, WrappedWatchableObject, boolean)}
*/
public void setObject(int index, WrappedWatchableObject value) {
setObject(index, value.getRawValue(), false);
}
/**
@@ -381,11 +461,19 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
*
* @param object Associated watcher object
* @param value Wrapped value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(WrappedDataWatcherObject, Object)}
*/
public void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value, boolean update) {
setObject(object, value.getRawValue(), update);
}
/**
* Shortcut for {@link #setObject(WrappedDataWatcherObject, WrappedWatchableObject, boolean)}
*/
public void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value) {
setObject(object, value.getRawValue());
setObject(object, value.getRawValue(), false);
}
/**
@@ -399,27 +487,46 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @throws IllegalArgumentException If the watcher object is null or must
* have a serializer and does not have one.
*/
public void setObject(WrappedDataWatcherObject object, Object value) {
public void setObject(WrappedDataWatcherObject object, Object value, boolean update) {
Validate.notNull(object, "Watcher object cannot be null!");
if (SETTER == null || REGISTER == null) {
if (SETTER == null && REGISTER == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
FuzzyMethodContract contract = FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.requireModifier(Modifier.PUBLIC)
.parameterExactArray(object.getHandleType(), Object.class)
.build();
SETTER = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "set"));
REGISTER = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "register"));
List<Method> methods = fuzzy.getMethodList(contract);
for (Method method : methods) {
if (method.getName().equals("set") || method.getName().equals("watch")) {
SETTER = Accessors.getMethodAccessor(method);
} else {
REGISTER = Accessors.getMethodAccessor(method);
}
}
}
// Unwrap the object
value = WrappedWatchableObject.getUnwrapped(value);
if (hasIndex(object.getIndex())) {
SETTER.invoke(handle, object.getHandle(), WrappedWatchableObject.getUnwrapped(value));
SETTER.invoke(handle, object.getHandle(), value);
} else {
Serializer serializer = object.getSerializer();
Validate.notNull(serializer, "You must specify a serializer to register an object!");
REGISTER.invoke(handle, object.getHandle(), WrappedWatchableObject.getUnwrapped(value));
object.checkSerializer();
REGISTER.invoke(handle, object.getHandle(), value);
}
if (update) {
getWatchableObject(object.getIndex()).setDirtyState(update);
}
}
/**
* Shortcut for {@link #setObject(WrappedDataWatcherObject, Object, boolean)}
*/
public void setObject(WrappedDataWatcherObject object, Object value) {
setObject(object, value, false);
}
// ---- Utility Methods
@@ -432,8 +539,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
public WrappedDataWatcher deepClone() {
WrappedDataWatcher clone = new WrappedDataWatcher(getEntity());
for (WrappedWatchableObject wrapper : this) {
clone.setObject(wrapper.getWatcherObject(), wrapper);
if (MinecraftReflection.watcherObjectExists()) {
for (WrappedWatchableObject wrapper : this) {
clone.setObject(wrapper.getWatcherObject(), wrapper);
}
} else {
for (WrappedWatchableObject wrapper : this) {
clone.setObject(wrapper.getIndex(), wrapper);
}
}
return clone;
@@ -479,26 +592,37 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
ENTITY_FIELD.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity));
}
private static final ImmutableBiMap<Class<?>, Integer> CLASS_TO_ID = new ImmutableBiMap.Builder<Class<?>, Integer>()
.put(Byte.class, 0)
.put(Short.class, 1)
.put(Integer.class, 2)
.put(Float.class, 3)
.put(String.class, 4)
.put(MinecraftReflection.getItemStackClass(), 5)
.put(MinecraftReflection.getBlockPositionClass(), 6)
.put(Vector3F.getMinecraftClass(), 7)
.build();
/**
* No longer supported in 1.9 due to the removal of a consistent type <-> ID map.
* Retrieves the type ID associated with a given class. No longer supported
* in 1.9 and up due to the removal of type IDs.
*
* @param clazz
* @return Null
* @param clazz Class to find ID for
* @return The ID, or null if not found
*/
@Deprecated
public static Integer getTypeID(Class<?> clazz) {
return null;
return CLASS_TO_ID.get(clazz);
}
/**
* No longer supported in 1.9 due to the removal of a consistent type <-> ID map.
* Retrieves the class associated with a given type ID. No longer
* supported in 1.9 and up due to the removal of type IDs.
*
* @param typeID
* @return Null
* @param typeID ID to find Class for
* @return The Class, or null if not found
*/
@Deprecated
public static Class<?> getTypeClass(int typeID) {
return null;
return CLASS_TO_ID.inverse().get(typeID);
}
@Override
@@ -544,12 +668,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
*
* @author dmulloy2
*/
public static class WrappedDataWatcherObject extends AbstractWrapper {
public static class WrappedDataWatcherObject {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherObjectClass();
private static ConstructorAccessor constructor = null;
private static MethodAccessor getSerializer = null;
private final StructureModifier<Object> modifier;
private StructureModifier<Object> modifier;
private Object handle;
protected WrappedDataWatcherObject() {
}
/**
* Creates a new watcher object from a NMS handle.
@@ -557,9 +685,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @param handle The handle
*/
public WrappedDataWatcherObject(Object handle) {
super(HANDLE_TYPE);
setHandle(handle);
this.handle = handle;
this.modifier = new StructureModifier<Object>(HANDLE_TYPE).withTarget(handle);
}
@@ -573,6 +699,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
this(newHandle(index, serializer));
}
static WrappedDataWatcherObject fromIndex(int index) {
if (MinecraftReflection.watcherObjectExists()) {
return new WrappedDataWatcherObject(index, null);
} else {
return new DummyWatcherObject(index);
}
}
private static Object newHandle(int index, Serializer serializer) {
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
@@ -616,6 +750,18 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
public void checkSerializer() {
Validate.notNull(getSerializer(), "You must specify a serializer to register an object!");
}
public Object getHandle() {
return handle;
}
public Class<?> getHandleType() {
return HANDLE_TYPE;
}
@Override
public String toString() {
return "DataWatcherObject[index=" + getIndex() + ", serializer=" + getSerializer() + "]";
@@ -624,7 +770,6 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof WrappedDataWatcherObject) {
WrappedDataWatcherObject other = (WrappedDataWatcherObject) obj;
@@ -633,6 +778,57 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
}
private static class DummyWatcherObject extends WrappedDataWatcherObject {
private final int index;
public DummyWatcherObject(int index) {
this.index = index;
}
@Override
public int getIndex() {
return index;
}
@Override
public Serializer getSerializer() {
return null;
}
@Override
public Object getHandle() {
return getIndex();
}
@Override
public Class<?> getHandleType() {
return int.class;
}
@Override
public void checkSerializer() {
// Do nothing
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof DummyWatcherObject) {
DummyWatcherObject that = (DummyWatcherObject) obj;
return this.index == that.index;
}
return false;
}
}
/**
@@ -768,7 +964,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return The serializer, or null if none exists
*/
public static Serializer fromHandle(Object handle) {
Validate.notNull("Handle cannot be null!");
Validate.notNull("handle cannot be null!");
initialize();
for (Serializer serializer : REGISTRY) {
@@ -820,5 +1016,67 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
}
// ---- Helper methods
/**
* Gets the serializer for IChatBaseComponents
* @return The serializer
*/
public static Serializer getChatComponentSerializer() {
return get(MinecraftReflection.getIChatBaseComponentClass());
}
/**
* Gets the serializer for ItemStacks
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getItemStackSerializer(boolean optional) {
return get(MinecraftReflection.getItemStackClass(), optional);
}
/**
* Gets the serializer for BlockData
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getBlockDataSerializer(boolean optional) {
return get(MinecraftReflection.getIBlockDataClass(), optional);
}
/**
* Gets the serializer for Vector3Fs
* @return The serializer
*/
public static Serializer getVectorSerializer() {
return get(Vector3F.getMinecraftClass());
}
/**
* Gets the serializer for BlockPositions
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getBlockPositionSerializer(boolean optional) {
return get(MinecraftReflection.getBlockPositionClass(), optional);
}
/**
* Gets the serializer for Directions
* @return The serializer
*/
public static Serializer getDirectionSerializer() {
return get(EnumWrappers.getDirectionClass());
}
/**
* Gets the serializer for UUIDs
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getUUIDSerializer(boolean optional) {
return get(UUID.class, optional);
}
}
}
@@ -132,15 +132,16 @@ public class WrappedSignedProperty extends AbstractWrapper {
}
@Override
public boolean equals(Object object){
public boolean equals(Object object) {
if (object == this) return true;
if (object instanceof WrappedSignedProperty) {
if (!super.equals(object))
return false;
WrappedSignedProperty that = (WrappedSignedProperty) object;
return Objects.equal(this.getName(), that.getName())
&& Objects.equal(this.getValue(), that.getValue())
&& Objects.equal(this.getSignature(), that.getSignature());
}
return false;
}
@@ -82,21 +82,4 @@ public class WrappedStatistic extends AbstractWrapper {
public String toString() {
return String.valueOf(handle);
}
@Override
public int hashCode() {
return handle.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof WrappedGameProfile) {
WrappedStatistic other = (WrappedStatistic) obj;
return handle.equals(other.handle);
}
return false;
}
}
@@ -16,28 +16,33 @@
*/
package com.comphenix.protocol.wrappers;
import static com.comphenix.protocol.utility.MinecraftReflection.is;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import com.google.common.base.Optional;
/**
* Represents a DataWatcher Item in 1.9.
* Represents a DataWatcher Item in 1.8 thru 1.10.
* @author dmulloy2
*/
public class WrappedWatchableObject extends AbstractWrapper {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherItemClass();
private static Integer VALUE_INDEX = null;
private static ConstructorAccessor constructor;
private final StructureModifier<Object> modifier;
/**
* Constructs a wrapped watchable object around an existing NMS data watcher item.
* Constructs a DataWatcher Item wrapper from an existing NMS data watcher item.
* @param handle Data watcher item
*/
public WrappedWatchableObject(Object handle) {
@@ -47,7 +52,18 @@ public class WrappedWatchableObject extends AbstractWrapper {
}
/**
* Constructs a wrapped watchable object with a given watcher object and initial value.
* Constructs a DataWatcher Item wrapper from a given index and initial value.
* <p>
* Not recommended in 1.9 and up.
* @param index Index of the Item
* @param value Initial value
*/
public WrappedWatchableObject(int index, Object value) {
this(newHandle(WrappedDataWatcherObject.fromIndex(index), value));
}
/**
* Constructs a DataWatcher Item wrapper from a given watcher object and initial value.
* @param watcherObject Watcher object
* @param value Initial value
*/
@@ -60,7 +76,12 @@ public class WrappedWatchableObject extends AbstractWrapper {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
}
return constructor.invoke(watcherObject.getHandle(), value);
if (MinecraftReflection.watcherObjectExists()) {
return constructor.invoke(watcherObject.getHandle(), value);
} else {
// new WatchableObject(classId, index, value)
return constructor.invoke(WrappedDataWatcher.getTypeID(value.getClass()), watcherObject.getIndex(), value);
}
}
// ---- Getter methods
@@ -78,7 +99,11 @@ public class WrappedWatchableObject extends AbstractWrapper {
* @return The index
*/
public int getIndex() {
return getWatcherObject().getIndex();
if (MinecraftReflection.watcherObjectExists()) {
return getWatcherObject().getIndex();
}
return modifier.<Integer>withType(int.class).read(1);
}
/**
@@ -94,7 +119,11 @@ public class WrappedWatchableObject extends AbstractWrapper {
* @return Raw value
*/
public Object getRawValue() {
return modifier.readSafely(1);
if (VALUE_INDEX == null) {
VALUE_INDEX = MinecraftReflection.watcherObjectExists() ? 1 : 2;
}
return modifier.readSafely(VALUE_INDEX);
}
/**
@@ -103,7 +132,11 @@ public class WrappedWatchableObject extends AbstractWrapper {
* @param updateClient Whether or not to update the client
*/
public void setValue(Object value, boolean updateClient) {
modifier.write(1, getUnwrapped(value));
if (VALUE_INDEX == null) {
VALUE_INDEX = MinecraftReflection.watcherObjectExists() ? 1 : 2;
}
modifier.write(VALUE_INDEX, getUnwrapped(value));
if (updateClient) {
setDirtyState(true);
@@ -123,7 +156,7 @@ public class WrappedWatchableObject extends AbstractWrapper {
* @return True if it must, false if not
*/
public boolean getDirtyState() {
return (boolean) modifier.read(2);
return modifier.<Boolean>withType(boolean.class).read(0);
}
/**
@@ -131,41 +164,55 @@ public class WrappedWatchableObject extends AbstractWrapper {
* @param dirty New state
*/
public void setDirtyState(boolean dirty) {
modifier.write(2, dirty);
modifier.<Boolean>withType(boolean.class).write(0, dirty);
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof WrappedWatchableObject) {
// watcher object, value, dirty state
WrappedWatchableObject other = (WrappedWatchableObject) obj;
return getWatcherObject().equals(other.getWatcherObject())
&& getRawValue().equals(other.getRawValue())
&& getDirtyState() == other.getDirtyState();
WrappedWatchableObject that = (WrappedWatchableObject) obj;
return this.getIndex() == that.getIndex() &&
this.getRawValue().equals(that.getRawValue()) &&
this.getDirtyState() == that.getDirtyState();
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getIndex();
result = prime * result + getRawValue().hashCode();
result = prime * result + (getDirtyState() ? 1231 : 1237);
return result;
}
@Override
public String toString() {
return "DataWatcherItem[object=" + getWatcherObject() + ", value=" + getValue() + ", dirty=" + getDirtyState() + "]";
return "DataWatcherItem[index=" + getIndex() + ", value=" + getValue() + ", dirty=" + getDirtyState() + "]";
}
// ---- Wrapping
/**
* Retrieve the wrapped object value, if needed.
* Retrieve the wrapped object value, if needed. All non-primitive objects
* with {@link Serializer}s should be covered by this.
*
* @param value - the raw NMS object to wrap.
* @return The wrapped object.
*/
@SuppressWarnings("rawtypes")
static Object getWrapped(Object value) {
// Deal with optionals first
// Handle watcher items first
if (is(MinecraftReflection.getDataWatcherItemClass(), value)) {
return getWrapped(new WrappedWatchableObject(value).getRawValue());
}
// Then deal with optionals
if (value instanceof Optional) {
Optional<?> optional = (Optional<?>) value;
if (optional.isPresent()) {
@@ -175,17 +222,29 @@ public class WrappedWatchableObject extends AbstractWrapper {
}
}
if (MinecraftReflection.isItemStack(value)) {
// Current supported classes
if (is(MinecraftReflection.getIChatBaseComponentClass(), value)) {
return WrappedChatComponent.fromHandle(value);
} else if (is(MinecraftReflection.getItemStackClass(), value)) {
return BukkitConverters.getItemStackConverter().getSpecific(value);
} else if (MinecraftReflection.isChunkCoordinates(value)) {
return new WrappedChunkCoordinate((Comparable) value);
} else if (MinecraftReflection.isBlockPosition(value)) {
} else if (is(MinecraftReflection.getIBlockDataClass(), value)) {
return BukkitConverters.getWrappedBlockDataConverter().getSpecific(value);
} else if (is (Vector3F.getMinecraftClass(), value)) {
return Vector3F.getConverter().getSpecific(value);
} else if (is(MinecraftReflection.getBlockPositionClass(), value)) {
return BlockPosition.getConverter().getSpecific(value);
} else if (MinecraftReflection.isChunkPosition(value)) {
return ChunkPosition.getConverter().getSpecific(value);
} else {
return value;
} else if (is(EnumWrappers.getDirectionClass(), value)) {
return EnumWrappers.getDirectionConverter().getSpecific(value);
}
// Legacy classes
if (is(MinecraftReflection.getChunkCoordinatesClass(), value)) {
return new WrappedChunkCoordinate((Comparable) value);
} else if (is(MinecraftReflection.getChunkPositionClass(), value)) {
return ChunkPosition.getConverter().getSpecific(value);
}
return value;
}
/**
@@ -194,6 +253,7 @@ public class WrappedWatchableObject extends AbstractWrapper {
* @param wrapped - the wrapped position.
* @return The raw NMS object.
*/
// Must be kept in sync with getWrapped!
static Object getUnwrapped(Object wrapped) {
if (wrapped instanceof Optional) {
Optional<?> optional = (Optional<?>) wrapped;
@@ -204,16 +264,28 @@ public class WrappedWatchableObject extends AbstractWrapper {
}
}
if (wrapped instanceof ItemStack) {
// Current supported classes
if (wrapped instanceof WrappedChatComponent) {
return ((WrappedChatComponent) wrapped).getHandle();
} else if (wrapped instanceof ItemStack) {
return BukkitConverters.getItemStackConverter().getGeneric(MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
} else if (wrapped instanceof WrappedChunkCoordinate) {
return ((WrappedChunkCoordinate) wrapped).getHandle();
} else if (wrapped instanceof WrappedBlockData) {
return BukkitConverters.getWrappedBlockDataConverter().getGeneric(MinecraftReflection.getIBlockDataClass(), (WrappedBlockData) wrapped);
} else if (wrapped instanceof Vector3F) {
return Vector3F.getConverter().getGeneric(Vector3F.getMinecraftClass(), (Vector3F) wrapped);
} else if (wrapped instanceof BlockPosition) {
return BlockPosition.getConverter().getGeneric(MinecraftReflection.getBlockPositionClass(), (BlockPosition) wrapped);
} else if (wrapped instanceof ChunkPosition) {
return ChunkPosition.getConverter().getGeneric(MinecraftReflection.getChunkPositionClass(), (ChunkPosition) wrapped);
} else {
return wrapped;
} else if (wrapped instanceof Direction) {
return EnumWrappers.getDirectionConverter().getGeneric(EnumWrappers.getDirectionClass(), (Direction) wrapped);
}
// Legacy classes
if (wrapped instanceof ChunkPosition) {
return ChunkPosition.getConverter().getGeneric(MinecraftReflection.getChunkPositionClass(), (ChunkPosition) wrapped);
} else if (wrapped instanceof WrappedChunkCoordinate) {
return ((WrappedChunkCoordinate) wrapped).getHandle();
}
return wrapped;
}
}
@@ -46,7 +46,7 @@ class TileEntityAccessor<T extends BlockState> {
private boolean writeDetected;
private boolean readDetected;
private TileEntityAccessor() {
TileEntityAccessor() {
// Do nothing
}
@@ -59,27 +59,30 @@ class TileEntityAccessor<T extends BlockState> {
if (tileEntityField != null) {
this.tileEntityField = tileEntityField;
Class<?> type = tileEntityField.getField().getType();
// Possible read/write methods
try {
findMethodsUsingASM();
} catch (IOException ex1) {
try {
// Much slower though
findMethodUsingCGLib(state);
} catch (Exception ex2) {
throw new RuntimeException("Cannot find read/write methods in " + type, ex2);
}
}
// Ensure we found them
if (readCompound == null)
throw new RuntimeException("Unable to find read method in " + type);
if (writeCompound == null)
throw new RuntimeException("Unable to find write method in " + type);
findMethods(type, state);
}
}
void findMethods(Class<?> type, T state) {
// Possible read/write methods
try {
findMethodsUsingASM();
} catch (IOException ex1) {
try {
// Much slower though
findMethodUsingCGLib(state);
} catch (Exception ex2) {
throw new RuntimeException("Cannot find read/write methods in " + type, ex2);
}
}
// Ensure we found them
if (readCompound == null)
throw new RuntimeException("Unable to find read method in " + type);
if (writeCompound == null)
throw new RuntimeException("Unable to find write method in " + type);
}
/**
* Find the read/write methods in TileEntity.
* @throws IOException If we cannot find these methods.
@@ -90,7 +93,7 @@ class TileEntityAccessor<T extends BlockState> {
final ClassReader reader = new ClassReader(tileEntityClass.getCanonicalName());
final String tagCompoundName = getJarName(MinecraftReflection.getNBTCompoundClass());
final String expectedDesc = "(L" + tagCompoundName + ";)V";
final String expectedDesc = "(L" + tagCompoundName + ";)";
reader.accept(new EmptyClassVisitor() {
@Override
@@ -98,7 +101,7 @@ class TileEntityAccessor<T extends BlockState> {
final String methodName = name;
// Detect read/write calls to NBTTagCompound
if (expectedDesc.equals(desc)) {
if (desc.startsWith(expectedDesc)) {
return new EmptyMethodVisitor() {
private int readMethods;
private int writeMethods;
@@ -106,9 +109,9 @@ class TileEntityAccessor<T extends BlockState> {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
// This must be a virtual call on NBTTagCompound that accepts a String
if (opcode == Opcodes.INVOKEVIRTUAL &&
tagCompoundName.equals(owner) &&
desc.startsWith("(Ljava/lang/String")) {
if (opcode == Opcodes.INVOKEVIRTUAL
&& tagCompoundName.equals(owner)
&& desc.startsWith("(Ljava/lang/String")) {
// Is this a write call?
if (desc.endsWith(")V")) {
@@ -126,10 +129,12 @@ class TileEntityAccessor<T extends BlockState> {
} else if (writeMethods > readMethods) {
writeCompound = Accessors.getMethodAccessor(tileEntityClass, methodName, nbtCompoundClass);
}
super.visitEnd();
}
};
}
return null;
}
}, 0);
+2 -2
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>4.0.2</version>
<version>4.1.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
@@ -222,7 +222,7 @@
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-API</artifactId>
<version>4.0-SNAPSHOT</version>
<version>${project.version}</version>
</dependency>
<!-- Included with Minecraft
<dependency>
@@ -4,6 +4,7 @@ import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import com.comphenix.protocol.PacketType.Protocol;
@@ -53,7 +54,7 @@ class PacketTypeParser {
// And finally, parse packet names if we have a protocol
if (protocol != null) {
for (Iterator<String> it = arguments.iterator(); it.hasNext(); ) {
String name = it.next().toUpperCase();
String name = it.next().toUpperCase(Locale.ENGLISH);
Collection<PacketType> names = PacketType.fromName(name);
for (PacketType type : names) {
@@ -671,7 +671,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
MinecraftMethods.getSendPacketMethod().invoke(getPlayerConnection(), packet);
}
} catch (Throwable ex) {
ProtocolLibrary.getErrorReporter().reportWarning(factory.getPlugin(),
ProtocolLibrary.getErrorReporter().reportWarning(this,
Report.newBuilder(REPORT_CANNOT_SEND_PACKET).messageParam(packet, playerName).error(ex).build());
}
}
@@ -716,7 +716,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
*/
private Object getPlayerConnection() {
if (playerConnection == null) {
playerConnection = MinecraftFields.getPlayerConnection(player);
playerConnection = MinecraftFields.getPlayerConnection(getPlayer());
}
return playerConnection;
}
@@ -24,6 +24,7 @@ import net.minecraft.server.v1_10_R1.DispenserRegistry;
*/
public class BukkitInitialization {
private static boolean initialized;
private static boolean packaged;
/**
* Initialize Bukkit and ProtocolLib such that we can perfrom unit testing.
@@ -34,6 +35,8 @@ public class BukkitInitialization {
// Denote that we're done
initialized = true;
initializePackage();
DispenserRegistry.c(); // Basically registers everything
// Mock the server object
@@ -50,7 +53,7 @@ public class BukkitInitialization {
// Inject this fake server
Bukkit.setServer(mockedServer);
initializePackage();
}
}
@@ -58,8 +61,11 @@ public class BukkitInitialization {
* Ensure that package names are correctly set up.
*/
public static void initializePackage() {
// Initialize reflection
MinecraftReflection.setMinecraftPackage(Constants.NMS, Constants.OBC);
MinecraftVersion.setCurrentVersion(MinecraftVersion.COMBAT_UPDATE);
if (!packaged) {
packaged = true;
MinecraftReflection.setMinecraftPackage(Constants.NMS, Constants.OBC);
MinecraftVersion.setCurrentVersion(MinecraftVersion.FROSTBURN_UPDATE);
}
}
}
@@ -1,3 +1,19 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 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;
import static org.junit.Assert.assertEquals;
@@ -16,6 +32,9 @@ import com.comphenix.protocol.injector.netty.ProtocolRegistry;
import net.minecraft.server.v1_10_R1.PacketLoginInStart;
/**
* @author dmulloy2
*/
public class PacketTypeTest {
@BeforeClass
@@ -542,7 +542,7 @@ public class PacketContainerTest {
public void testDeepClone() {
// Try constructing all the packets
for (PacketType type : PacketType.values()) {
if (BLACKLISTED.contains(type)) {
if (BLACKLISTED.contains(type) || type.isDeprecated()) {
continue;
}
@@ -588,9 +588,7 @@ public class PacketContainerTest {
}
} catch (IllegalArgumentException e) {
if (!registered) {
// Let the test pass
System.err.println("The packet ID " + type + " is not registered.");
assertEquals(e.getMessage(), "The packet ID " + type + " is not registered.");
e.printStackTrace();
} else {
// Something is very wrong
throw e;
@@ -0,0 +1,53 @@
/**
* (c) 2016 dmulloy2
*/
package com.comphenix.protocol.injector;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.netty.WirePacket;
import io.netty.buffer.ByteBuf;
/**
* @author dmulloy2
*/
public class WirePacketTest {
@BeforeClass
public static void beforeClass() {
BukkitInitialization.initializePackage();
}
@Test
public void testPackets() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.CHAT);
WirePacket wire = WirePacket.fromPacket(packet);
WirePacket handle = WirePacket.fromPacket(packet.getHandle());
assertEquals(wire, handle);
}
@Test
public void testSerialization() {
int id = 42;
byte[] array = { 1, 3, 7, 21, 88, 67, 8 };
WirePacket packet = new WirePacket(id, array);
ByteBuf buf = packet.serialize();
int backId = WirePacket.readVarInt(buf);
byte[] backArray = new byte[buf.readableBytes()];
buf.readBytes(backArray);
assertEquals(id, backId);
assertArrayEquals(array, backArray);
}
}
@@ -0,0 +1,41 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 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.nbt;
import org.bukkit.block.BlockState;
import org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.BukkitInitialization;
/**
* @author dmulloy2
*/
public class TileEntityTest {
@BeforeClass
public static void beforeClass() {
BukkitInitialization.initializePackage();
}
@Test
public void test() {
// Ensure the read and write methods exist
TileEntityAccessor<BlockState> accessor = new TileEntityAccessor<>();
accessor.findMethods(null, null);
}
}