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 extends ChannelFuture> collection) {
- List extends ChannelFuture> copy = Lists.newArrayList(collection);
+ public boolean addAll(Collection extends Object> 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 extends ChannelFuture> 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 extends ChannelFuture> succededFuture =
+ (Class extends ChannelFuture>) 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