diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolManager.java index e009b589..72c5f36c 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolManager.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolManager.java @@ -153,12 +153,23 @@ public interface ProtocolManager extends PacketStream { /** * Construct a packet using the special builtin Minecraft constructors. + *

+ * Deprecated: Use {@link #createPacketConstructor(PacketType, Object...)} instead. * @param id - the packet ID. * @param arguments - arguments that will be passed to the constructor. * @return The packet constructor. */ + @Deprecated public PacketConstructor createPacketConstructor(int id, Object... arguments); + /** + * Construct a packet using the special builtin Minecraft constructors. + * @param id - the packet type. + * @param arguments - arguments that will be passed to the constructor. + * @return The packet constructor. + */ + public PacketConstructor createPacketConstructor(PacketType type, Object... arguments); + /** * Completely resend an entity to a list of clients. *

diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/DelayedPacketManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/DelayedPacketManager.java index c00a003b..dab294de 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/DelayedPacketManager.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/DelayedPacketManager.java @@ -15,6 +15,7 @@ import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import com.comphenix.protocol.AsynchronousManager; +import com.comphenix.protocol.PacketType; import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.error.Report; @@ -318,6 +319,7 @@ public class DelayedPacketManager implements ProtocolManager, InternalManager { } } + @SuppressWarnings("deprecation") @Override public PacketConstructor createPacketConstructor(int id, Object... arguments) { if (delegate != null) @@ -325,6 +327,14 @@ public class DelayedPacketManager implements ProtocolManager, InternalManager { else return PacketConstructor.DEFAULT.withPacket(id, arguments); } + + @Override + public PacketConstructor createPacketConstructor(PacketType type, Object... arguments) { + if (delegate != null) + return delegate.createPacketConstructor(type, arguments); + else + return PacketConstructor.DEFAULT.withPacket(type, arguments); + } @Override public Set getSendingFilters() { @@ -416,4 +426,6 @@ public class DelayedPacketManager implements ProtocolManager, InternalManager { delegate.close(); closed = true; } + + } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java index dbdde496..c7a0fcd4 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java @@ -48,6 +48,7 @@ import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import com.comphenix.protocol.AsynchronousManager; +import com.comphenix.protocol.PacketType; import com.comphenix.protocol.Packets; import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.async.AsyncFilterManager; @@ -236,7 +237,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok // Use the correct injection type if (MinecraftReflection.isUsingNetty()) { - this.nettyInjector = new NettyProtocolInjector(this); + this.nettyInjector = new NettyProtocolInjector(this, reporter); this.playerInjection = nettyInjector.getPlayerInjector(); this.packetInjector = nettyInjector.getPacketInjector(); @@ -834,10 +835,16 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok } @Override + @Deprecated public PacketConstructor createPacketConstructor(int id, Object... arguments) { return PacketConstructor.DEFAULT.withPacket(id, arguments); } + @Override + public PacketConstructor createPacketConstructor(PacketType type, Object... arguments) { + return PacketConstructor.DEFAULT.withPacket(type, arguments); + } + @Override public Set getSendingFilters() { return playerInjection.getSendingFilters(); @@ -1145,6 +1152,8 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok packetInjector.cleanupAll(); if (spigotInjector != null) spigotInjector.cleanupAll(); + if (nettyInjector != null) + nettyInjector.close(); // Remove server handler playerInjection.close(); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/BootstrapList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/BootstrapList.java index 74251f0b..11c17037 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/BootstrapList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/BootstrapList.java @@ -2,6 +2,7 @@ package com.comphenix.protocol.injector.netty; import java.util.Collection; import java.util.List; +import java.util.NoSuchElementException; import com.google.common.collect.ForwardingList; import com.google.common.collect.Lists; @@ -10,8 +11,8 @@ import com.google.common.collect.Lists; import net.minecraft.util.io.netty.channel.ChannelFuture; import net.minecraft.util.io.netty.channel.ChannelHandler; -class BootstrapList extends ForwardingList { - private List delegate; +class BootstrapList extends ForwardingList { + private List delegate; private ChannelHandler handler; /** @@ -19,59 +20,79 @@ class BootstrapList extends ForwardingList { * @param delegate - the delegate. * @param handler - the channel handler to add. */ - public BootstrapList(List delegate, ChannelHandler handler) { + public BootstrapList(List delegate, ChannelHandler handler) { this.delegate = delegate; this.handler = handler; // Process all existing bootstraps - for (ChannelFuture future : this) - processBootstrap(future); + for (Object item : this) { + if (item instanceof ChannelFuture) { + processBootstrap((ChannelFuture) item); + } + } } @Override - protected List delegate() { + protected List delegate() { return delegate; } @Override - public boolean add(ChannelFuture element) { - processBootstrap(element); + public boolean add(Object element) { + processElement(element); return super.add(element); } @Override - public boolean addAll(Collection collection) { - List copy = Lists.newArrayList(collection); + public boolean addAll(Collection collection) { + List copy = Lists.newArrayList(collection); // Process the collection before we pass it on - for (ChannelFuture future : copy) { - processBootstrap(future); + for (Object element : copy) { + processElement(element); } return super.addAll(copy); } @Override - public ChannelFuture set(int index, ChannelFuture element) { - ChannelFuture old = super.set(index, element); + public Object set(int index, Object element) { + Object old = super.set(index, element); // Handle the old future, and the newly inserted future if (old != element) { - if (old != null) { - unprocessBootstrap(old); - } - if (element != null) { - processBootstrap(element); - } + unprocessElement(old); + processElement(element); } return old; } + /** + * Process a single element. + * @param element - the element. + */ + protected void processElement(Object element) { + if (element instanceof ChannelFuture) { + processBootstrap((ChannelFuture) element); + } + } + + /** + * Unprocess a single element. + * @param element - the element to unprocess. + */ + protected void unprocessElement(Object element) { + if (element instanceof ChannelFuture) { + unprocessBootstrap((ChannelFuture) element); + } + } + /** * Process a single channel future. * @param future - the future. */ protected void processBootstrap(ChannelFuture future) { - future.channel().pipeline().addLast(handler); + // Important: Must be addFirst() + future.channel().pipeline().addFirst(handler); } /** @@ -79,14 +100,18 @@ class BootstrapList extends ForwardingList { * @param future - the future. */ protected void unprocessBootstrap(ChannelFuture future) { - future.channel().pipeline().remove(handler); + try { + future.channel().pipeline().remove(handler); + } catch (NoSuchElementException e) { + // Whatever + } } /** * Close and revert all changes. */ public void close() { - for (ChannelFuture future : this) - unprocessBootstrap(future); + for (Object element : this) + unprocessElement(element); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java index 12471568..74736b51 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java @@ -6,9 +6,11 @@ import java.net.SocketAddress; import java.util.Collections; import java.util.List; import java.util.Map.Entry; +import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.ConcurrentMap; +import net.minecraft.util.com.google.common.base.Joiner; import net.minecraft.util.io.netty.buffer.ByteBuf; import net.minecraft.util.io.netty.channel.Channel; import net.minecraft.util.io.netty.channel.ChannelHandler; @@ -17,12 +19,16 @@ import net.minecraft.util.io.netty.channel.socket.SocketChannel; import net.minecraft.util.io.netty.handler.codec.ByteToMessageDecoder; import net.minecraft.util.io.netty.handler.codec.MessageToByteEncoder; import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener; +import net.minecraft.util.io.netty.util.internal.TypeParameterMatcher; import net.sf.cglib.proxy.Factory; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import com.comphenix.protocol.PacketType.Protocol; +import com.comphenix.protocol.error.ErrorReporter; +import com.comphenix.protocol.error.Report; +import com.comphenix.protocol.error.ReportType; import com.comphenix.protocol.events.ConnectionSide; import com.comphenix.protocol.events.NetworkMarker; import com.comphenix.protocol.events.PacketEvent; @@ -45,6 +51,9 @@ import com.google.common.collect.MapMaker; * @author Kristian */ class ChannelInjector extends ByteToMessageDecoder { + public static final ReportType REPORT_CANNOT_INTERCEPT_SERVER_PACKET = new ReportType("Unable to intercept a written server packet."); + public static final ReportType REPORT_CANNOT_INTERCEPT_CLIENT_PACKET = new ReportType("Unable to intercept a read client packet."); + /** * Represents a listener for received or sent packets. * @author Kristian @@ -78,18 +87,29 @@ class ChannelInjector extends ByteToMessageDecoder { * @return TRUE if we do, FALSE otherwise. */ public boolean includeBuffer(int packetId); + + /** + * Retrieve the current error reporter. + * @return The error reporter. + */ + public ErrorReporter getReporter(); } private static final ConcurrentMap cachedInjector = new MapMaker().weakKeys().makeMap(); + // Saved accessors + private static MethodAccessor DECODE_BUFFER; + private static MethodAccessor ENCODE_BUFFER; + private static FieldAccessor ENCODER_TYPE_MATCHER; + + // For retrieving the protocol + private static FieldAccessor PROTOCOL_ACCESSOR; + // The player, or temporary player private Player player; // The player connection private Object playerConnection; - - // For retrieving the protocol - private FieldAccessor protocolAccessor; // The current network manager and channel private final Object networkManager; @@ -106,8 +126,6 @@ class ChannelInjector extends ByteToMessageDecoder { // Other handlers private ByteToMessageDecoder vanillaDecoder; private MessageToByteEncoder vanillaEncoder; - private MethodAccessor decodeBuffer; - private MethodAccessor encodeBuffer; // Our extra handler private MessageToByteEncoder protocolEncoder; @@ -130,16 +148,18 @@ class ChannelInjector extends ByteToMessageDecoder { this.networkManager = Preconditions.checkNotNull(networkManager, "networkMananger cannot be NULL"); this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL"); this.channelListener = Preconditions.checkNotNull(channelListener, "channelListener cannot be NULL"); - + // Get the channel field this.channelField = new VolatileField( FuzzyReflection.fromObject(networkManager, true). - getFieldByType("channel", Channel.class), networkManager); + getFieldByType("channel", Channel.class), + networkManager, true); } /** * Construct or retrieve a channel injector from an existing Bukkit player. * @param player - the existing Bukkit player. + * @param channelListener - the listener. * @return A new injector, or an existing injector associated with this player. */ public static ChannelInjector fromPlayer(Player player, ChannelListener listener) { @@ -169,6 +189,8 @@ class ChannelInjector extends ByteToMessageDecoder { * Construct a new channel injector for the given channel. * @param channel - the channel. * @param playerFactory - a temporary player creator. + * @param channelListener - the listener. + * @param loader - the current (plugin) class loader. * @return The channel injector. */ public static ChannelInjector fromChannel(Channel channel, ChannelListener listener, TemporaryPlayerFactory playerFactory) { @@ -201,48 +223,81 @@ class ChannelInjector extends ByteToMessageDecoder { // Get the vanilla decoder, so we don't have to replicate the work vanillaDecoder = (ByteToMessageDecoder) originalChannel.pipeline().get("decoder"); vanillaEncoder = (MessageToByteEncoder) originalChannel.pipeline().get("encoder"); - decodeBuffer = FuzzyReflection.getMethodAccessor(vanillaDecoder.getClass(), + patchEncoder(vanillaEncoder); + + if (vanillaDecoder == null) + throw new IllegalArgumentException("Unable to find vanilla decoder.in " + originalChannel.pipeline()); + if (vanillaEncoder == null) + throw new IllegalArgumentException("Unable to find vanilla encoder in " + originalChannel.pipeline()); + + if (DECODE_BUFFER == null) + DECODE_BUFFER = FuzzyReflection.getMethodAccessor(vanillaDecoder.getClass(), "decode", ChannelHandlerContext.class, ByteBuf.class, List.class); - encodeBuffer = FuzzyReflection.getMethodAccessor(vanillaEncoder.getClass(), + if (ENCODE_BUFFER == null) + ENCODE_BUFFER = FuzzyReflection.getMethodAccessor(vanillaEncoder.getClass(), "encode", ChannelHandlerContext.class, Object.class, ByteBuf.class); protocolEncoder = new MessageToByteEncoder() { @Override protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception { - NetworkMarker marker = getMarker(output); - PacketEvent event = markerEvent.remove(marker); - - if (event != null && NetworkMarker.hasOutputHandlers(marker)) { - ByteBuf packetBuffer = ctx.alloc().buffer(); - encodeBuffer.invoke(vanillaEncoder, ctx, packet, packetBuffer); - byte[] data = getBytes(packetBuffer); + try { + NetworkMarker marker = getMarker(output); + PacketEvent event = markerEvent.remove(marker); - for (PacketOutputHandler handler : marker.getOutputHandlers()) { - handler.handle(event, data); + if (event != null && NetworkMarker.hasOutputHandlers(marker)) { + ByteBuf packetBuffer = ctx.alloc().buffer(); + ENCODE_BUFFER.invoke(vanillaEncoder, ctx, packet, packetBuffer); + byte[] data = getBytes(packetBuffer); + + for (PacketOutputHandler handler : marker.getOutputHandlers()) { + handler.handle(event, data); + } + // Write the result + output.writeBytes(data); + return; } - // Write the result - output.writeBytes(data); + } catch (Exception e) { + channelListener.getReporter().reportDetailed(this, + Report.newBuilder(REPORT_CANNOT_INTERCEPT_SERVER_PACKET).callerParam(packet).error(e).build()); + } finally { + // Attempt to handle the packet nevertheless + ENCODE_BUFFER.invoke(vanillaEncoder, ctx, packet, output); } } + + public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) { + throwable.printStackTrace(); + } }; - // Insert our handler - note that we replace the decoder with our own + // Insert our handlers - note that we effectively replace the vanilla encoder/decoder originalChannel.pipeline().addBefore("decoder", "protocol_lib_decoder", this); originalChannel.pipeline().addAfter("encoder", "protocol_lib_encoder", protocolEncoder); // Intercept all write methods - channelField.setValue(new ChannelProxy() { + channelField.setValue(new ChannelProxy(originalChannel) { @Override protected Object onMessageWritten(Object message) { return channelListener.onPacketSending(ChannelInjector.this, message, packetMarker.get(message)); } - }.asChannel(originalChannel)); + }); injected = true; return true; } } + /** + * This method patches the encoder so that it skips already created packets. + * @param encoder - the encoder to patch. + */ + private void patchEncoder(MessageToByteEncoder encoder) { + if (ENCODER_TYPE_MATCHER == null) { + ENCODER_TYPE_MATCHER = FuzzyReflection.getFieldAccessor(encoder.getClass(), "matcher", true); + } + ENCODER_TYPE_MATCHER.set(encoder, TypeParameterMatcher.get(MinecraftReflection.getPacketClass())); + } + /** * Close the current injector. */ @@ -251,34 +306,54 @@ class ChannelInjector extends ByteToMessageDecoder { closed = true; if (injected) { - originalChannel.pipeline().remove(this); - originalChannel.pipeline().remove(protocolEncoder); channelField.revertValue(); + + try { + originalChannel.pipeline().remove(this); + originalChannel.pipeline().remove(protocolEncoder); + } catch (NoSuchElementException e) { + // Ignore it - the player has logged out + } } } } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List packets) throws Exception { - byteBuffer.markReaderIndex(); - decodeBuffer.invoke(vanillaDecoder, ctx, byteBuffer, packets); - - if (packets.size() > 0) { - Object input = packets.get(0); - int id = PacketRegistry.getPacketID(input.getClass()); - NetworkMarker marker = null; + try { + byteBuffer.markReaderIndex(); + DECODE_BUFFER.invoke(vanillaDecoder, ctx, byteBuffer, packets); - if (channelListener.includeBuffer(id)) { - byteBuffer.resetReaderIndex(); - marker = new NetworkMarker(ConnectionSide.CLIENT_SIDE, getBytes(byteBuffer)); + if (packets.size() > 0) { + Object input = packets.get(0); + int id = PacketRegistry.getPacketID(input.getClass()); + NetworkMarker marker = null; + + if (channelListener.includeBuffer(id)) { + byteBuffer.resetReaderIndex(); + marker = new NetworkMarker(ConnectionSide.CLIENT_SIDE, getBytes(byteBuffer)); + } + Object output = channelListener.onPacketReceiving(this, input, marker); + + // Handle packet changes + if (output == null) + packets.clear(); + else if (output != input) + packets.set(0, output); } - Object output = channelListener.onPacketReceiving(this, input, marker); - - // Handle packet changes - if (output == null) - packets.clear(); - else if (output != input) - packets.set(0, output); + } catch (Exception e) { + channelListener.getReporter().reportDetailed(this, + Report.newBuilder(REPORT_CANNOT_INTERCEPT_CLIENT_PACKET).callerParam(byteBuffer).error(e).build()); + } + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + super.channelActive(ctx); + + // See NetworkManager.channelActive(ChannelHandlerContext) for why + if (channelField != null) { + channelField.refreshValue(); } } @@ -364,11 +439,11 @@ class ChannelInjector extends ByteToMessageDecoder { * @return The current protocol. */ public Protocol getCurrentProtocol() { - if (protocolAccessor == null) { - protocolAccessor = FuzzyReflection.getFieldAccessor( + if (PROTOCOL_ACCESSOR == null) { + PROTOCOL_ACCESSOR = FuzzyReflection.getFieldAccessor( networkManager.getClass(), MinecraftReflection.getEnumProtocolClass(), true); } - return Protocol.fromVanilla((Enum) protocolAccessor.get(networkManager)); + return Protocol.fromVanilla((Enum) PROTOCOL_ACCESSOR.get(networkManager)); } /** diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelProxy.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelProxy.java index 9d72dc17..801b1168 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelProxy.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelProxy.java @@ -1,57 +1,236 @@ package com.comphenix.protocol.injector.netty; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Set; +import java.lang.reflect.Constructor; +import java.net.SocketAddress; -import com.comphenix.protocol.reflect.FuzzyReflection; -import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; - -import net.minecraft.util.com.google.common.collect.Sets; +import net.minecraft.util.io.netty.buffer.ByteBufAllocator; import net.minecraft.util.io.netty.channel.Channel; -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.MethodInterceptor; -import net.sf.cglib.proxy.MethodProxy; +import net.minecraft.util.io.netty.channel.ChannelConfig; +import net.minecraft.util.io.netty.channel.ChannelFuture; +import net.minecraft.util.io.netty.channel.ChannelMetadata; +import net.minecraft.util.io.netty.channel.ChannelPipeline; +import net.minecraft.util.io.netty.channel.ChannelProgressivePromise; +import net.minecraft.util.io.netty.channel.ChannelPromise; +import net.minecraft.util.io.netty.channel.EventLoop; +import net.minecraft.util.io.netty.util.Attribute; +import net.minecraft.util.io.netty.util.AttributeKey; +import net.minecraft.util.io.netty.util.concurrent.EventExecutor; -abstract class ChannelProxy { - private static Set WRITE_METHODS; +abstract class ChannelProxy implements Channel { + private static Constructor FUTURE_CONSTRUCTOR; - /** - * Retrieve the channel proxy object. - * @param proxyInstance - the proxy instance object. - * @return The channel proxy. - */ - public Channel asChannel(final Channel proxyInstance) { - // Simple way to match all the write methods - if (WRITE_METHODS == null) { - List writers = FuzzyReflection.fromClass(Channel.class). - getMethodList(FuzzyMethodContract.newBuilder().nameRegex("write.*").build()); - WRITE_METHODS = Sets.newHashSet(writers); - } - - Enhancer enhancer = new Enhancer(); - enhancer.setSuperclass(Channel.class); - enhancer.setCallback(new MethodInterceptor() { - @Override - public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { - if (WRITE_METHODS.contains(method)) { - args[0] = onMessageWritten(args[0]); - - // If we should skip this object - if (args[0] == null) - return null; - } - // Forward to proxy - return proxy.invoke(proxyInstance, args); - } - }); - return (Channel) enhancer.create(); + // The underlying channel + private Channel delegate; + + public ChannelProxy(Channel delegate) { + this.delegate = delegate; } - + /** * Invoked when a packet is being transmitted. * @param message - the packet to transmit. * @return The object to transmit. */ protected abstract Object onMessageWritten(Object message); + + /** + * The future we return when packets are being cancelled. + * @return A succeeded future. + */ + protected ChannelFuture getSucceededFuture() { + try { + if (FUTURE_CONSTRUCTOR == null) { + @SuppressWarnings("unchecked") + Class succededFuture = + (Class) ChannelProxy.class.getClassLoader(). + loadClass("net.minecraft.util.io.netty.channel.SucceededChannelFuture"); + + FUTURE_CONSTRUCTOR = succededFuture.getConstructor(Channel.class, EventExecutor.class); + } + return FUTURE_CONSTRUCTOR.newInstance(this, null); + + } catch (ClassNotFoundException e) { + throw new RuntimeException("Cannot get succeeded future."); + } catch (Exception e) { + throw new RuntimeException("Cannot construct completed future."); + } + } + + public Attribute attr(AttributeKey paramAttributeKey) { + return delegate.attr(paramAttributeKey); + } + + public ChannelFuture bind(SocketAddress paramSocketAddress) { + return delegate.bind(paramSocketAddress); + } + + public ChannelPipeline pipeline() { + return delegate.pipeline(); + } + + public ChannelFuture connect(SocketAddress paramSocketAddress) { + return delegate.connect(paramSocketAddress); + } + + public ByteBufAllocator alloc() { + return delegate.alloc(); + } + + public ChannelPromise newPromise() { + return delegate.newPromise(); + } + + public EventLoop eventLoop() { + return delegate.eventLoop(); + } + + public ChannelFuture connect(SocketAddress paramSocketAddress1, + SocketAddress paramSocketAddress2) { + return delegate.connect(paramSocketAddress1, paramSocketAddress2); + } + + public ChannelProgressivePromise newProgressivePromise() { + return delegate.newProgressivePromise(); + } + + public Channel parent() { + return delegate.parent(); + } + + public ChannelConfig config() { + return delegate.config(); + } + + public ChannelFuture newSucceededFuture() { + return delegate.newSucceededFuture(); + } + + public boolean isOpen() { + return delegate.isOpen(); + } + + public ChannelFuture disconnect() { + return delegate.disconnect(); + } + + public boolean isRegistered() { + return delegate.isRegistered(); + } + + public ChannelFuture newFailedFuture(Throwable paramThrowable) { + return delegate.newFailedFuture(paramThrowable); + } + + public ChannelFuture close() { + return delegate.close(); + } + + public boolean isActive() { + return delegate.isActive(); + } + + @Deprecated + public ChannelFuture deregister() { + return delegate.deregister(); + } + + public ChannelPromise voidPromise() { + return delegate.voidPromise(); + } + + public ChannelMetadata metadata() { + return delegate.metadata(); + } + + public ChannelFuture bind(SocketAddress paramSocketAddress, + ChannelPromise paramChannelPromise) { + return delegate.bind(paramSocketAddress, paramChannelPromise); + } + + public SocketAddress localAddress() { + return delegate.localAddress(); + } + + public SocketAddress remoteAddress() { + return delegate.remoteAddress(); + } + + public ChannelFuture connect(SocketAddress paramSocketAddress, + ChannelPromise paramChannelPromise) { + return delegate.connect(paramSocketAddress, paramChannelPromise); + } + + public ChannelFuture closeFuture() { + return delegate.closeFuture(); + } + + public boolean isWritable() { + return delegate.isWritable(); + } + + public Channel flush() { + return delegate.flush(); + } + + public ChannelFuture connect(SocketAddress paramSocketAddress1, + SocketAddress paramSocketAddress2, ChannelPromise paramChannelPromise) { + return delegate.connect(paramSocketAddress1, paramSocketAddress2, paramChannelPromise); + } + + public Channel read() { + return delegate.read(); + } + + public Unsafe unsafe() { + return delegate.unsafe(); + } + + public ChannelFuture disconnect(ChannelPromise paramChannelPromise) { + return delegate.disconnect(paramChannelPromise); + } + + public ChannelFuture close(ChannelPromise paramChannelPromise) { + return delegate.close(paramChannelPromise); + } + + @Deprecated + public ChannelFuture deregister(ChannelPromise paramChannelPromise) { + return delegate.deregister(paramChannelPromise); + } + + public ChannelFuture write(Object message) { + Object result = onMessageWritten(message); + + if (result != null) + return delegate.write(result); + return getSucceededFuture(); + } + + public ChannelFuture write(Object message, ChannelPromise paramChannelPromise) { + Object result = onMessageWritten(message); + + if (result != null) + return delegate.write(message, paramChannelPromise); + return getSucceededFuture(); + } + + public ChannelFuture writeAndFlush(Object message, ChannelPromise paramChannelPromise) { + Object result = onMessageWritten(message); + + if (result != null) + return delegate.writeAndFlush(message, paramChannelPromise); + return getSucceededFuture(); + } + + public ChannelFuture writeAndFlush(Object message) { + Object result = onMessageWritten(message); + + if (result != null) + return delegate.writeAndFlush(message); + return getSucceededFuture(); + } + + public int compareTo(Channel o) { + return delegate.compareTo(o); + } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/NettyProtocolInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/NettyProtocolInjector.java index 6b062049..5567aa45 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/NettyProtocolInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/NettyProtocolInjector.java @@ -20,6 +20,7 @@ import net.minecraft.util.io.netty.channel.ChannelInitializer; import com.comphenix.protocol.Packets; import com.comphenix.protocol.concurrency.IntegerSet; +import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.ConnectionSide; import com.comphenix.protocol.events.NetworkMarker; import com.comphenix.protocol.events.PacketContainer; @@ -34,6 +35,7 @@ import com.comphenix.protocol.injector.spigot.AbstractPlayerHandler; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.utility.MinecraftReflection; +import com.google.common.collect.Lists; public class NettyProtocolInjector implements ChannelListener { private volatile boolean injected; @@ -41,7 +43,7 @@ public class NettyProtocolInjector implements ChannelListener { // The temporary player factory private TemporaryPlayerFactory playerFactory = new TemporaryPlayerFactory(); - private VolatileField bootstrapField; + private List bootstrapFields = Lists.newArrayList(); // Different sending filters private IntegerSet queuedFilters = new IntegerSet(Packets.PACKET_COUNT); @@ -49,11 +51,14 @@ public class NettyProtocolInjector implements ChannelListener { // Which packets are buffered private Set bufferedPackets; - private ListenerInvoker invoker; - public NettyProtocolInjector(ListenerInvoker invoker) { + // Handle errors + private ErrorReporter reporter; + + public NettyProtocolInjector(ListenerInvoker invoker, ErrorReporter reporter) { this.invoker = invoker; + this.reporter = reporter; } /** @@ -89,7 +94,7 @@ public class NettyProtocolInjector implements ChannelListener { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { Channel channel = (Channel) msg; - + // Execute the other handlers before adding our own ctx.fireChannelRead(msg); channel.pipeline().addLast(initProtocol); @@ -97,11 +102,14 @@ public class NettyProtocolInjector implements ChannelListener { }; // Insert ProtocolLib's connection interceptor - bootstrapField = getBootstrapField(serverConnection); - bootstrapField.setValue(new BootstrapList( - (List) bootstrapField.getValue(), connectionHandler - )); + bootstrapFields = getBootstrapFields(serverConnection); + for (VolatileField field : bootstrapFields) { + field.setValue(new BootstrapList( + (List) field.getValue(), connectionHandler + )); + } + injected = true; } catch (Exception e) { @@ -109,6 +117,11 @@ public class NettyProtocolInjector implements ChannelListener { } } + @Override + public ErrorReporter getReporter() { + return reporter; + } + /** * Inject our packet handling into a specific player. * @param player @@ -117,23 +130,20 @@ public class NettyProtocolInjector implements ChannelListener { ChannelInjector.fromPlayer(player, this).inject(); } - private VolatileField getBootstrapField(Object serverConnection) { - VolatileField firstVolatile = null; + private List getBootstrapFields(Object serverConnection) { + List result = Lists.newArrayList(); + // Find and (possibly) proxy every list for (Field field : FuzzyReflection.fromObject(serverConnection, true).getFieldListByType(List.class)) { - VolatileField currentVolatile = new VolatileField(field, serverConnection, true); + VolatileField volatileField = new VolatileField(field, serverConnection, true); @SuppressWarnings("unchecked") - List list = (List) currentVolatile.getValue(); + List list = (List) volatileField.getValue(); - // Also save the first list - if (firstVolatile == null) { - firstVolatile = currentVolatile; - } - if (list.size() > 0 && list.get(0) instanceof ChannelFuture) { - return currentVolatile; + if (list.size() == 0 || list.get(0) instanceof ChannelFuture) { + result.add(volatileField); } } - return firstVolatile; + return result; } /** @@ -142,22 +152,21 @@ public class NettyProtocolInjector implements ChannelListener { public synchronized void close() { if (!closed) { closed = true; - - @SuppressWarnings("unchecked") - List bootstraps = (List) bootstrapField.getValue(); - - // Remember to close all the bootstraps - for (Object value : bootstraps) { + + for (VolatileField field : bootstrapFields) { + Object value = field.getValue(); + + // Undo the processed channels, if any if (value instanceof BootstrapList) { ((BootstrapList) value).close(); } + field.revertValue(); } // Uninject all the players for (Player player : Bukkit.getServer().getOnlinePlayers()) { ChannelInjector.fromPlayer(player, this).close(); } - bootstrapField.revertValue(); } } @@ -261,12 +270,14 @@ public class NettyProtocolInjector implements ChannelListener { @Override public void sendServerPacket(Player reciever, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException { - ChannelInjector.fromPlayer(reciever, listener).sendServerPacket(packet.getHandle(), marker, filters); + ChannelInjector.fromPlayer(reciever, listener). + sendServerPacket(packet.getHandle(), marker, filters); } @Override public void recieveClientPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException { - ChannelInjector.fromPlayer(player, listener).recieveClientPacket(mcPacket, null, true); + ChannelInjector.fromPlayer(player, listener). + recieveClientPacket(mcPacket, null, true); } @Override @@ -296,7 +307,8 @@ public class NettyProtocolInjector implements ChannelListener { @Override public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) { NetworkMarker marker = buffered != null ? new NetworkMarker(ConnectionSide.CLIENT_SIDE, buffered) : null; - ChannelInjector.fromPlayer(client, NettyProtocolInjector.this).saveMarker(packet.getHandle(), marker); + ChannelInjector.fromPlayer(client, NettyProtocolInjector.this). + saveMarker(packet.getHandle(), marker); return packetReceived(packet, client, marker); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/server/TemporaryPlayerFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/server/TemporaryPlayerFactory.java index 968efbf3..83aa6f6b 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/server/TemporaryPlayerFactory.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/server/TemporaryPlayerFactory.java @@ -30,23 +30,17 @@ import net.sf.cglib.proxy.NoOp; import org.bukkit.Server; import org.bukkit.entity.Player; -import com.comphenix.protocol.injector.PacketConstructor; +import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.reflect.FieldAccessException; +import com.comphenix.protocol.utility.ChatExtensions; /** * Create fake player instances that represents pre-authenticated clients. */ public class TemporaryPlayerFactory { - // Helpful constructors - private final PacketConstructor chatPacket; - // Prevent too many class creations private static CallbackFilter callbackFilter; - public TemporaryPlayerFactory() { - chatPacket = PacketConstructor.DEFAULT.withPacket(3, new Object[] { "DEMO" }); - } - /** * Retrieve the injector from a given player if it contains one. * @param player - the player that may contain a reference to a player injector. @@ -190,7 +184,9 @@ public class TemporaryPlayerFactory { * @throws FieldAccessException If we were unable to construct the message packet. */ private Object sendMessage(SocketInjector injector, String message) throws InvocationTargetException, FieldAccessException { - injector.sendServerPacket(chatPacket.createPacket(message).getHandle(), null, false); + for (PacketContainer packet : ChatExtensions.createChatPackets(message)) { + injector.sendServerPacket(packet.getHandle(), null, false); + } return null; } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java index 68879b39..9b93a0b3 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java @@ -21,6 +21,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; @@ -29,9 +30,11 @@ import java.util.Map; import java.util.Set; import java.util.regex.Pattern; +import net.minecraft.util.com.google.common.base.Joiner; import net.minecraft.util.com.google.common.collect.Sets; import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher; +import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -135,10 +138,22 @@ public class FuzzyReflection { */ public static FieldAccessor getFieldAccessor(Class instanceClass, Class fieldClass, boolean forceAccess) { // Get a field accessor - Field field = FuzzyReflection.fromObject(instanceClass, forceAccess).getFieldByType(null, fieldClass); + Field field = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldByType(null, fieldClass); return getFieldAccessor(field); } + /** + * Retrieve an accessor for the first field of the given type. + * @param instanceClass - the type of the instance to retrieve. + * @param fieldClass - type of the field to retrieve. + * @param forceAccess - whether or not to look for private and protected fields. + * @return The value of that field. + * @throws IllegalArgumentException If the field cannot be found. + */ + public static FieldAccessor getFieldAccessor(Class instanceClass, String fieldName, boolean forceAccess) { + return getFieldAccessor(FieldUtils.getField(instanceClass, fieldName, forceAccess)); + } + /** * Retrieve a field accessor from a given field that uses unchecked exceptions. * @param field - the field. @@ -186,9 +201,30 @@ public class FuzzyReflection { * @return The method accessor. */ public static MethodAccessor getMethodAccessor(Class instanceClass, String name, Class... parameters) { - Method method = MethodUtils.getAccessibleMethod(instanceClass, name, parameters); - method.setAccessible(true); - return getMethodAccessor(method); + return getMethodAccessor(instanceClass, instanceClass, name, parameters); + } + + // Helper method + private static MethodAccessor getMethodAccessor( + Class initialClass, Class instanceClass, String name, Class... parameters) { + + try { + Method method = instanceClass.getDeclaredMethod(name, parameters); + method.setAccessible(true); + return getMethodAccessor(method); + + } catch (NoSuchMethodException e) { + // Search for a private method in the superclass + if (initialClass.getSuperclass() != null) + return getMethodAccessor(initialClass, instanceClass.getSuperclass(), name, parameters); + + // Unable to find it + throw new IllegalArgumentException("Unable to find method " + name + + "(" + Joiner.on(", ").join(parameters) +") in " + initialClass); + + } catch (Exception e) { + throw new RuntimeException("Unable to retrieve methods.", e); + } } /** @@ -244,7 +280,13 @@ public class FuzzyReflection { Field field = null; try { - method = getMethodByParameters("getInstance", source.getClass(), new Class[0]); + method = getMethod( + FuzzyMethodContract.newBuilder(). + parameterCount(0). + returnDerivedOf(source). + requireModifier(Modifier.STATIC). + build() + ); } catch (IllegalArgumentException e) { // Try getting the field instead // Note that this will throw an exception if not found diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/VolatileField.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/VolatileField.java index 3ae21863..f67c7143 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/VolatileField.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/VolatileField.java @@ -122,7 +122,6 @@ public class VolatileField { * @param newValue - new field value. */ public void setValue(Object newValue) { - // Remember to safe the previous value ensureLoaded(); @@ -132,7 +131,20 @@ public class VolatileField { currentSet = true; } catch (IllegalAccessException e) { - throw new RuntimeException("Unable to read field " + field.getName(), e); + throw new RuntimeException("Unable to write field " + field.getName(), e); + } + } + + /** + * Ensure the previously set value is set. + */ + public void refreshValue() { + if (currentSet) { + try { + FieldUtils.writeField(field, container, current, forceAccess); + } catch (IllegalAccessException e) { + throw new RuntimeException("Unable to read field " + field.getName(), e); + } } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java index 16aaf9e8..dff9895f 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java @@ -19,7 +19,6 @@ package com.comphenix.protocol.utility; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; @@ -27,12 +26,14 @@ import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import com.comphenix.protocol.Packets; +import com.comphenix.protocol.PacketType; import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.injector.PacketConstructor; import com.comphenix.protocol.injector.packet.PacketRegistry; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.FuzzyReflection.MethodAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.google.common.base.Strings; @@ -45,12 +46,14 @@ import com.google.common.collect.Iterables; */ public class ChatExtensions { // Used to sent chat messages - private PacketConstructor chatConstructor; private ProtocolManager manager; + // The chat packet constructor + private static volatile PacketConstructor chatConstructor; + // Whether or not we have to use the post-1.6.1 chat format - private static Constructor jsonConstructor = getJsonFormatConstructor(); - private static Method messageFactory; + private static volatile Constructor jsonConstructor = getJsonFormatConstructor(); + private static volatile MethodAccessor messageFactory; public ChatExtensions(ProtocolManager manager) { this.manager = manager; @@ -83,63 +86,64 @@ public class ChatExtensions { * @throws InvocationTargetException If we were unable to send the message. */ private void sendMessageSilently(Player player, String message) throws InvocationTargetException { - if (jsonConstructor != null) { - sendMessageAsJson(player, message); - } else { - sendMessageAsString(player, message); + try { + for (PacketContainer packet : createChatPackets(message)) { + manager.sendServerPacket(player, packet, false); + } + } catch (FieldAccessException e) { + throw new InvocationTargetException(e); } } /** - * Send a message using the new JSON format in 1.6.1. - * @param player - the player to send it to. + * Construct chat packet to send in order to display a given message. * @param message - the message to send. - * @throws InvocationTargetException InvocationTargetException If we were unable to send the message. + * @return The packets. */ - private void sendMessageAsJson(Player player, String message) throws InvocationTargetException { - Object messageObject = null; + public static PacketContainer[] createChatPackets(String message) { + if (jsonConstructor != null) { + if (chatConstructor == null) { + Class messageClass = jsonConstructor.getParameterTypes()[0]; + chatConstructor = PacketConstructor.DEFAULT.withPacket(PacketType.Play.Server.CHAT, new Object[] { messageClass }); + + // Try one of the string constructors + if (MinecraftReflection.isUsingNetty()) { + messageFactory = FuzzyReflection.getMethodAccessor( + MinecraftReflection.getCraftMessageClass(), "fromString", String.class); + } else { + messageFactory = FuzzyReflection.getMethodAccessor( + FuzzyReflection.fromClass(messageClass).getMethod( + FuzzyMethodContract.newBuilder(). + requireModifier(Modifier.STATIC). + parameterCount(1). + parameterExactType(String.class). + returnTypeMatches(FuzzyMatchers.matchParent()). + build()) + ); + } + } + + // Minecraft 1.7.2 and later + if (MinecraftReflection.isUsingNetty()) { + Object[] components = (Object[]) messageFactory.invoke(null, message); + PacketContainer[] packets = new PacketContainer[components.length]; + + for (int i = 0; i < components.length; i++) { + packets[i] = chatConstructor.createPacket(components[i]); + } + return packets; + + // Minecraft 1.6.1 - 1.6.4 + } else { + return new PacketContainer[] { chatConstructor.createPacket(messageFactory.invoke(null, message)) }; + } - if (chatConstructor == null) { - Class messageClass = jsonConstructor.getParameterTypes()[0]; - chatConstructor = manager.createPacketConstructor(Packets.Server.CHAT, messageClass); - - // Try one of the string constructors - messageFactory = FuzzyReflection.fromClass(messageClass).getMethod( - FuzzyMethodContract.newBuilder(). - requireModifier(Modifier.STATIC). - parameterCount(1). - parameterExactType(String.class). - returnTypeMatches(FuzzyMatchers.matchParent()). - build()); - } - - try { - messageObject = messageFactory.invoke(null, message); - } catch (Exception e) { - throw new InvocationTargetException(e); - } - - try { - manager.sendServerPacket(player, chatConstructor.createPacket(messageObject), false); - } catch (FieldAccessException e) { - throw new InvocationTargetException(e); - } - } - - /** - * Send a message as a pure string. - * @param player - the player. - * @param message - the message to send. - * @throws InvocationTargetException If anything went wrong. - */ - private void sendMessageAsString(Player player, String message) throws InvocationTargetException { - if (chatConstructor == null) - chatConstructor = manager.createPacketConstructor(Packets.Server.CHAT, message); - - try { - manager.sendServerPacket(player, chatConstructor.createPacket(message), false); - } catch (FieldAccessException e) { - throw new InvocationTargetException(e); + } else { + if (chatConstructor == null) { + chatConstructor = PacketConstructor.DEFAULT.withPacket(PacketType.Play.Server.CHAT, new Object[] { message }); + } + // Minecraft 1.6.0 and earlier + return new PacketContainer[] { chatConstructor.createPacket(message) }; } } @@ -210,7 +214,7 @@ public class ChatExtensions { * @return A constructor for JSON-based packets. */ static Constructor getJsonFormatConstructor() { - Class chatPacket = PacketRegistry.getPacketClassFromID(3, true); + Class chatPacket = PacketRegistry.getPacketClassFromType(PacketType.Play.Server.CHAT, true); List> list = FuzzyReflection.fromClass(chatPacket).getConstructorList( FuzzyMethodContract.newBuilder(). parameterCount(1). diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java index 5cccb5ed..ad179409 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java @@ -1,14 +1,10 @@ package com.comphenix.protocol.utility; -import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.Proxy; import java.util.List; import java.util.Map; -import org.bukkit.command.defaults.EnchantCommand; - import net.minecraft.util.io.netty.buffer.ByteBuf; import net.minecraft.util.io.netty.buffer.UnpooledByteBufAllocator; import net.minecraft.util.io.netty.channel.ChannelHandlerContext; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index 27762e84..1bd9770e 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -1366,6 +1366,14 @@ public class MinecraftReflection { public static Class getCraftEntityClass() { return getCraftBukkitClass("entity.CraftEntity"); } + + /** + * Retrieve the CraftChatMessage introduced in 1.7.2 + * @return The CraftChatMessage class. + */ + public static Class getCraftMessageClass() { + return getCraftBukkitClass("util.CraftChatMessage"); + } /** * Retrieve a CraftItemStack from a given ItemStack. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java index de40185e..968901f8 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java @@ -7,7 +7,6 @@ import java.io.DataInputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.IOException; -import java.lang.reflect.Method; import javax.annotation.Nonnull; import org.bukkit.inventory.ItemStack; diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/BukkitInitialization.java b/ProtocolLib/src/test/java/com/comphenix/protocol/BukkitInitialization.java index 3ac4b374..cf054c09 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/BukkitInitialization.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/BukkitInitialization.java @@ -6,7 +6,6 @@ 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.RegistryMaterials; import net.minecraft.server.v1_7_R1.StatisticList; // Will have to be updated for every version though