mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-30 22:53:26 +01:00
Finalize the Netty injection system.
We still have some more debugging and refactoring left, however. In particular, I have to switch to PacketType in wherever possible.
This commit is contained in:
parent
7b813fa4e6
commit
6842166b94
@ -153,12 +153,23 @@ public interface ProtocolManager extends PacketStream {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a packet using the special builtin Minecraft constructors.
|
* Construct a packet using the special builtin Minecraft constructors.
|
||||||
|
* <p>
|
||||||
|
* Deprecated: Use {@link #createPacketConstructor(PacketType, Object...)} instead.
|
||||||
* @param id - the packet ID.
|
* @param id - the packet ID.
|
||||||
* @param arguments - arguments that will be passed to the constructor.
|
* @param arguments - arguments that will be passed to the constructor.
|
||||||
* @return The packet constructor.
|
* @return The packet constructor.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public PacketConstructor createPacketConstructor(int id, Object... arguments);
|
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.
|
* Completely resend an entity to a list of clients.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -15,6 +15,7 @@ import org.bukkit.plugin.Plugin;
|
|||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
|
||||||
import com.comphenix.protocol.AsynchronousManager;
|
import com.comphenix.protocol.AsynchronousManager;
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.ProtocolManager;
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
import com.comphenix.protocol.error.ErrorReporter;
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
import com.comphenix.protocol.error.Report;
|
import com.comphenix.protocol.error.Report;
|
||||||
@ -318,6 +319,7 @@ public class DelayedPacketManager implements ProtocolManager, InternalManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public PacketConstructor createPacketConstructor(int id, Object... arguments) {
|
public PacketConstructor createPacketConstructor(int id, Object... arguments) {
|
||||||
if (delegate != null)
|
if (delegate != null)
|
||||||
@ -325,6 +327,14 @@ public class DelayedPacketManager implements ProtocolManager, InternalManager {
|
|||||||
else
|
else
|
||||||
return PacketConstructor.DEFAULT.withPacket(id, arguments);
|
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
|
@Override
|
||||||
public Set<Integer> getSendingFilters() {
|
public Set<Integer> getSendingFilters() {
|
||||||
@ -416,4 +426,6 @@ public class DelayedPacketManager implements ProtocolManager, InternalManager {
|
|||||||
delegate.close();
|
delegate.close();
|
||||||
closed = true;
|
closed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ import org.bukkit.plugin.Plugin;
|
|||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
|
||||||
import com.comphenix.protocol.AsynchronousManager;
|
import com.comphenix.protocol.AsynchronousManager;
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.Packets;
|
||||||
import com.comphenix.protocol.ProtocolManager;
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
import com.comphenix.protocol.async.AsyncFilterManager;
|
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||||
@ -236,7 +237,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
|
|
||||||
// Use the correct injection type
|
// Use the correct injection type
|
||||||
if (MinecraftReflection.isUsingNetty()) {
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
this.nettyInjector = new NettyProtocolInjector(this);
|
this.nettyInjector = new NettyProtocolInjector(this, reporter);
|
||||||
this.playerInjection = nettyInjector.getPlayerInjector();
|
this.playerInjection = nettyInjector.getPlayerInjector();
|
||||||
this.packetInjector = nettyInjector.getPacketInjector();
|
this.packetInjector = nettyInjector.getPacketInjector();
|
||||||
|
|
||||||
@ -834,10 +835,16 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public PacketConstructor createPacketConstructor(int id, Object... arguments) {
|
public PacketConstructor createPacketConstructor(int id, Object... arguments) {
|
||||||
return PacketConstructor.DEFAULT.withPacket(id, arguments);
|
return PacketConstructor.DEFAULT.withPacket(id, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketConstructor createPacketConstructor(PacketType type, Object... arguments) {
|
||||||
|
return PacketConstructor.DEFAULT.withPacket(type, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Integer> getSendingFilters() {
|
public Set<Integer> getSendingFilters() {
|
||||||
return playerInjection.getSendingFilters();
|
return playerInjection.getSendingFilters();
|
||||||
@ -1145,6 +1152,8 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
packetInjector.cleanupAll();
|
packetInjector.cleanupAll();
|
||||||
if (spigotInjector != null)
|
if (spigotInjector != null)
|
||||||
spigotInjector.cleanupAll();
|
spigotInjector.cleanupAll();
|
||||||
|
if (nettyInjector != null)
|
||||||
|
nettyInjector.close();
|
||||||
|
|
||||||
// Remove server handler
|
// Remove server handler
|
||||||
playerInjection.close();
|
playerInjection.close();
|
||||||
|
@ -2,6 +2,7 @@ package com.comphenix.protocol.injector.netty;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
import com.google.common.collect.ForwardingList;
|
import com.google.common.collect.ForwardingList;
|
||||||
import com.google.common.collect.Lists;
|
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.ChannelFuture;
|
||||||
import net.minecraft.util.io.netty.channel.ChannelHandler;
|
import net.minecraft.util.io.netty.channel.ChannelHandler;
|
||||||
|
|
||||||
class BootstrapList extends ForwardingList<ChannelFuture> {
|
class BootstrapList extends ForwardingList<Object> {
|
||||||
private List<ChannelFuture> delegate;
|
private List<Object> delegate;
|
||||||
private ChannelHandler handler;
|
private ChannelHandler handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,59 +20,79 @@ class BootstrapList extends ForwardingList<ChannelFuture> {
|
|||||||
* @param delegate - the delegate.
|
* @param delegate - the delegate.
|
||||||
* @param handler - the channel handler to add.
|
* @param handler - the channel handler to add.
|
||||||
*/
|
*/
|
||||||
public BootstrapList(List<ChannelFuture> delegate, ChannelHandler handler) {
|
public BootstrapList(List<Object> delegate, ChannelHandler handler) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
|
|
||||||
// Process all existing bootstraps
|
// Process all existing bootstraps
|
||||||
for (ChannelFuture future : this)
|
for (Object item : this) {
|
||||||
processBootstrap(future);
|
if (item instanceof ChannelFuture) {
|
||||||
|
processBootstrap((ChannelFuture) item);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<ChannelFuture> delegate() {
|
protected List<Object> delegate() {
|
||||||
return delegate;
|
return delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean add(ChannelFuture element) {
|
public boolean add(Object element) {
|
||||||
processBootstrap(element);
|
processElement(element);
|
||||||
return super.add(element);
|
return super.add(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addAll(Collection<? extends ChannelFuture> collection) {
|
public boolean addAll(Collection<? extends Object> collection) {
|
||||||
List<? extends ChannelFuture> copy = Lists.newArrayList(collection);
|
List<Object> copy = Lists.newArrayList(collection);
|
||||||
|
|
||||||
// Process the collection before we pass it on
|
// Process the collection before we pass it on
|
||||||
for (ChannelFuture future : copy) {
|
for (Object element : copy) {
|
||||||
processBootstrap(future);
|
processElement(element);
|
||||||
}
|
}
|
||||||
return super.addAll(copy);
|
return super.addAll(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture set(int index, ChannelFuture element) {
|
public Object set(int index, Object element) {
|
||||||
ChannelFuture old = super.set(index, element);
|
Object old = super.set(index, element);
|
||||||
|
|
||||||
// Handle the old future, and the newly inserted future
|
// Handle the old future, and the newly inserted future
|
||||||
if (old != element) {
|
if (old != element) {
|
||||||
if (old != null) {
|
unprocessElement(old);
|
||||||
unprocessBootstrap(old);
|
processElement(element);
|
||||||
}
|
|
||||||
if (element != null) {
|
|
||||||
processBootstrap(element);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return old;
|
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.
|
* Process a single channel future.
|
||||||
* @param future - the future.
|
* @param future - the future.
|
||||||
*/
|
*/
|
||||||
protected void processBootstrap(ChannelFuture 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<ChannelFuture> {
|
|||||||
* @param future - the future.
|
* @param future - the future.
|
||||||
*/
|
*/
|
||||||
protected void unprocessBootstrap(ChannelFuture 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.
|
* Close and revert all changes.
|
||||||
*/
|
*/
|
||||||
public void close() {
|
public void close() {
|
||||||
for (ChannelFuture future : this)
|
for (Object element : this)
|
||||||
unprocessBootstrap(future);
|
unprocessElement(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@ import java.net.SocketAddress;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
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.buffer.ByteBuf;
|
||||||
import net.minecraft.util.io.netty.channel.Channel;
|
import net.minecraft.util.io.netty.channel.Channel;
|
||||||
import net.minecraft.util.io.netty.channel.ChannelHandler;
|
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.ByteToMessageDecoder;
|
||||||
import net.minecraft.util.io.netty.handler.codec.MessageToByteEncoder;
|
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.concurrent.GenericFutureListener;
|
||||||
|
import net.minecraft.util.io.netty.util.internal.TypeParameterMatcher;
|
||||||
import net.sf.cglib.proxy.Factory;
|
import net.sf.cglib.proxy.Factory;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.PacketType.Protocol;
|
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.ConnectionSide;
|
||||||
import com.comphenix.protocol.events.NetworkMarker;
|
import com.comphenix.protocol.events.NetworkMarker;
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
@ -45,6 +51,9 @@ import com.google.common.collect.MapMaker;
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
class ChannelInjector extends ByteToMessageDecoder {
|
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.
|
* Represents a listener for received or sent packets.
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
@ -78,18 +87,29 @@ class ChannelInjector extends ByteToMessageDecoder {
|
|||||||
* @return TRUE if we do, FALSE otherwise.
|
* @return TRUE if we do, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean includeBuffer(int packetId);
|
public boolean includeBuffer(int packetId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the current error reporter.
|
||||||
|
* @return The error reporter.
|
||||||
|
*/
|
||||||
|
public ErrorReporter getReporter();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final ConcurrentMap<Player, ChannelInjector> cachedInjector = new MapMaker().weakKeys().makeMap();
|
private static final ConcurrentMap<Player, ChannelInjector> 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
|
// The player, or temporary player
|
||||||
private Player player;
|
private Player player;
|
||||||
|
|
||||||
// The player connection
|
// The player connection
|
||||||
private Object playerConnection;
|
private Object playerConnection;
|
||||||
|
|
||||||
// For retrieving the protocol
|
|
||||||
private FieldAccessor protocolAccessor;
|
|
||||||
|
|
||||||
// The current network manager and channel
|
// The current network manager and channel
|
||||||
private final Object networkManager;
|
private final Object networkManager;
|
||||||
@ -106,8 +126,6 @@ class ChannelInjector extends ByteToMessageDecoder {
|
|||||||
// Other handlers
|
// Other handlers
|
||||||
private ByteToMessageDecoder vanillaDecoder;
|
private ByteToMessageDecoder vanillaDecoder;
|
||||||
private MessageToByteEncoder<Object> vanillaEncoder;
|
private MessageToByteEncoder<Object> vanillaEncoder;
|
||||||
private MethodAccessor decodeBuffer;
|
|
||||||
private MethodAccessor encodeBuffer;
|
|
||||||
|
|
||||||
// Our extra handler
|
// Our extra handler
|
||||||
private MessageToByteEncoder<Object> protocolEncoder;
|
private MessageToByteEncoder<Object> protocolEncoder;
|
||||||
@ -130,16 +148,18 @@ class ChannelInjector extends ByteToMessageDecoder {
|
|||||||
this.networkManager = Preconditions.checkNotNull(networkManager, "networkMananger cannot be NULL");
|
this.networkManager = Preconditions.checkNotNull(networkManager, "networkMananger cannot be NULL");
|
||||||
this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL");
|
this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL");
|
||||||
this.channelListener = Preconditions.checkNotNull(channelListener, "channelListener cannot be NULL");
|
this.channelListener = Preconditions.checkNotNull(channelListener, "channelListener cannot be NULL");
|
||||||
|
|
||||||
// Get the channel field
|
// Get the channel field
|
||||||
this.channelField = new VolatileField(
|
this.channelField = new VolatileField(
|
||||||
FuzzyReflection.fromObject(networkManager, true).
|
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.
|
* Construct or retrieve a channel injector from an existing Bukkit player.
|
||||||
* @param player - the 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.
|
* @return A new injector, or an existing injector associated with this player.
|
||||||
*/
|
*/
|
||||||
public static ChannelInjector fromPlayer(Player player, ChannelListener listener) {
|
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.
|
* Construct a new channel injector for the given channel.
|
||||||
* @param channel - the channel.
|
* @param channel - the channel.
|
||||||
* @param playerFactory - a temporary player creator.
|
* @param playerFactory - a temporary player creator.
|
||||||
|
* @param channelListener - the listener.
|
||||||
|
* @param loader - the current (plugin) class loader.
|
||||||
* @return The channel injector.
|
* @return The channel injector.
|
||||||
*/
|
*/
|
||||||
public static ChannelInjector fromChannel(Channel channel, ChannelListener listener, TemporaryPlayerFactory playerFactory) {
|
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
|
// Get the vanilla decoder, so we don't have to replicate the work
|
||||||
vanillaDecoder = (ByteToMessageDecoder) originalChannel.pipeline().get("decoder");
|
vanillaDecoder = (ByteToMessageDecoder) originalChannel.pipeline().get("decoder");
|
||||||
vanillaEncoder = (MessageToByteEncoder<Object>) originalChannel.pipeline().get("encoder");
|
vanillaEncoder = (MessageToByteEncoder<Object>) 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);
|
"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);
|
"encode", ChannelHandlerContext.class, Object.class, ByteBuf.class);
|
||||||
|
|
||||||
protocolEncoder = new MessageToByteEncoder<Object>() {
|
protocolEncoder = new MessageToByteEncoder<Object>() {
|
||||||
@Override
|
@Override
|
||||||
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
|
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
|
||||||
NetworkMarker marker = getMarker(output);
|
try {
|
||||||
PacketEvent event = markerEvent.remove(marker);
|
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);
|
|
||||||
|
|
||||||
for (PacketOutputHandler handler : marker.getOutputHandlers()) {
|
if (event != null && NetworkMarker.hasOutputHandlers(marker)) {
|
||||||
handler.handle(event, data);
|
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
|
} catch (Exception e) {
|
||||||
output.writeBytes(data);
|
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().addBefore("decoder", "protocol_lib_decoder", this);
|
||||||
originalChannel.pipeline().addAfter("encoder", "protocol_lib_encoder", protocolEncoder);
|
originalChannel.pipeline().addAfter("encoder", "protocol_lib_encoder", protocolEncoder);
|
||||||
|
|
||||||
// Intercept all write methods
|
// Intercept all write methods
|
||||||
channelField.setValue(new ChannelProxy() {
|
channelField.setValue(new ChannelProxy(originalChannel) {
|
||||||
@Override
|
@Override
|
||||||
protected Object onMessageWritten(Object message) {
|
protected Object onMessageWritten(Object message) {
|
||||||
return channelListener.onPacketSending(ChannelInjector.this, message, packetMarker.get(message));
|
return channelListener.onPacketSending(ChannelInjector.this, message, packetMarker.get(message));
|
||||||
}
|
}
|
||||||
}.asChannel(originalChannel));
|
});
|
||||||
|
|
||||||
injected = true;
|
injected = true;
|
||||||
return 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<Object> 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.
|
* Close the current injector.
|
||||||
*/
|
*/
|
||||||
@ -251,34 +306,54 @@ class ChannelInjector extends ByteToMessageDecoder {
|
|||||||
closed = true;
|
closed = true;
|
||||||
|
|
||||||
if (injected) {
|
if (injected) {
|
||||||
originalChannel.pipeline().remove(this);
|
|
||||||
originalChannel.pipeline().remove(protocolEncoder);
|
|
||||||
channelField.revertValue();
|
channelField.revertValue();
|
||||||
|
|
||||||
|
try {
|
||||||
|
originalChannel.pipeline().remove(this);
|
||||||
|
originalChannel.pipeline().remove(protocolEncoder);
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// Ignore it - the player has logged out
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List<Object> packets) throws Exception {
|
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List<Object> packets) throws Exception {
|
||||||
byteBuffer.markReaderIndex();
|
try {
|
||||||
decodeBuffer.invoke(vanillaDecoder, ctx, byteBuffer, packets);
|
byteBuffer.markReaderIndex();
|
||||||
|
DECODE_BUFFER.invoke(vanillaDecoder, ctx, byteBuffer, packets);
|
||||||
if (packets.size() > 0) {
|
|
||||||
Object input = packets.get(0);
|
|
||||||
int id = PacketRegistry.getPacketID(input.getClass());
|
|
||||||
NetworkMarker marker = null;
|
|
||||||
|
|
||||||
if (channelListener.includeBuffer(id)) {
|
if (packets.size() > 0) {
|
||||||
byteBuffer.resetReaderIndex();
|
Object input = packets.get(0);
|
||||||
marker = new NetworkMarker(ConnectionSide.CLIENT_SIDE, getBytes(byteBuffer));
|
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);
|
} catch (Exception e) {
|
||||||
|
channelListener.getReporter().reportDetailed(this,
|
||||||
// Handle packet changes
|
Report.newBuilder(REPORT_CANNOT_INTERCEPT_CLIENT_PACKET).callerParam(byteBuffer).error(e).build());
|
||||||
if (output == null)
|
}
|
||||||
packets.clear();
|
}
|
||||||
else if (output != input)
|
|
||||||
packets.set(0, output);
|
@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.
|
* @return The current protocol.
|
||||||
*/
|
*/
|
||||||
public Protocol getCurrentProtocol() {
|
public Protocol getCurrentProtocol() {
|
||||||
if (protocolAccessor == null) {
|
if (PROTOCOL_ACCESSOR == null) {
|
||||||
protocolAccessor = FuzzyReflection.getFieldAccessor(
|
PROTOCOL_ACCESSOR = FuzzyReflection.getFieldAccessor(
|
||||||
networkManager.getClass(), MinecraftReflection.getEnumProtocolClass(), true);
|
networkManager.getClass(), MinecraftReflection.getEnumProtocolClass(), true);
|
||||||
}
|
}
|
||||||
return Protocol.fromVanilla((Enum<?>) protocolAccessor.get(networkManager));
|
return Protocol.fromVanilla((Enum<?>) PROTOCOL_ACCESSOR.get(networkManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,57 +1,236 @@
|
|||||||
package com.comphenix.protocol.injector.netty;
|
package com.comphenix.protocol.injector.netty;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.List;
|
import java.net.SocketAddress;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import net.minecraft.util.io.netty.buffer.ByteBufAllocator;
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
|
||||||
|
|
||||||
import net.minecraft.util.com.google.common.collect.Sets;
|
|
||||||
import net.minecraft.util.io.netty.channel.Channel;
|
import net.minecraft.util.io.netty.channel.Channel;
|
||||||
import net.sf.cglib.proxy.Enhancer;
|
import net.minecraft.util.io.netty.channel.ChannelConfig;
|
||||||
import net.sf.cglib.proxy.MethodInterceptor;
|
import net.minecraft.util.io.netty.channel.ChannelFuture;
|
||||||
import net.sf.cglib.proxy.MethodProxy;
|
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 {
|
abstract class ChannelProxy implements Channel {
|
||||||
private static Set<Method> WRITE_METHODS;
|
private static Constructor<? extends ChannelFuture> FUTURE_CONSTRUCTOR;
|
||||||
|
|
||||||
/**
|
// The underlying channel
|
||||||
* Retrieve the channel proxy object.
|
private Channel delegate;
|
||||||
* @param proxyInstance - the proxy instance object.
|
|
||||||
* @return The channel proxy.
|
public ChannelProxy(Channel delegate) {
|
||||||
*/
|
this.delegate = delegate;
|
||||||
public Channel asChannel(final Channel proxyInstance) {
|
|
||||||
// Simple way to match all the write methods
|
|
||||||
if (WRITE_METHODS == null) {
|
|
||||||
List<Method> 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when a packet is being transmitted.
|
* Invoked when a packet is being transmitted.
|
||||||
* @param message - the packet to transmit.
|
* @param message - the packet to transmit.
|
||||||
* @return The object to transmit.
|
* @return The object to transmit.
|
||||||
*/
|
*/
|
||||||
protected abstract Object onMessageWritten(Object message);
|
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 <T> Attribute<T> attr(AttributeKey<T> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import net.minecraft.util.io.netty.channel.ChannelInitializer;
|
|||||||
|
|
||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.Packets;
|
||||||
import com.comphenix.protocol.concurrency.IntegerSet;
|
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||||
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
import com.comphenix.protocol.events.ConnectionSide;
|
import com.comphenix.protocol.events.ConnectionSide;
|
||||||
import com.comphenix.protocol.events.NetworkMarker;
|
import com.comphenix.protocol.events.NetworkMarker;
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
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.FuzzyReflection;
|
||||||
import com.comphenix.protocol.reflect.VolatileField;
|
import com.comphenix.protocol.reflect.VolatileField;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
public class NettyProtocolInjector implements ChannelListener {
|
public class NettyProtocolInjector implements ChannelListener {
|
||||||
private volatile boolean injected;
|
private volatile boolean injected;
|
||||||
@ -41,7 +43,7 @@ public class NettyProtocolInjector implements ChannelListener {
|
|||||||
|
|
||||||
// The temporary player factory
|
// The temporary player factory
|
||||||
private TemporaryPlayerFactory playerFactory = new TemporaryPlayerFactory();
|
private TemporaryPlayerFactory playerFactory = new TemporaryPlayerFactory();
|
||||||
private VolatileField bootstrapField;
|
private List<VolatileField> bootstrapFields = Lists.newArrayList();
|
||||||
|
|
||||||
// Different sending filters
|
// Different sending filters
|
||||||
private IntegerSet queuedFilters = new IntegerSet(Packets.PACKET_COUNT);
|
private IntegerSet queuedFilters = new IntegerSet(Packets.PACKET_COUNT);
|
||||||
@ -49,11 +51,14 @@ public class NettyProtocolInjector implements ChannelListener {
|
|||||||
|
|
||||||
// Which packets are buffered
|
// Which packets are buffered
|
||||||
private Set<Integer> bufferedPackets;
|
private Set<Integer> bufferedPackets;
|
||||||
|
|
||||||
private ListenerInvoker invoker;
|
private ListenerInvoker invoker;
|
||||||
|
|
||||||
public NettyProtocolInjector(ListenerInvoker invoker) {
|
// Handle errors
|
||||||
|
private ErrorReporter reporter;
|
||||||
|
|
||||||
|
public NettyProtocolInjector(ListenerInvoker invoker, ErrorReporter reporter) {
|
||||||
this.invoker = invoker;
|
this.invoker = invoker;
|
||||||
|
this.reporter = reporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +94,7 @@ public class NettyProtocolInjector implements ChannelListener {
|
|||||||
@Override
|
@Override
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
Channel channel = (Channel) msg;
|
Channel channel = (Channel) msg;
|
||||||
|
|
||||||
// Execute the other handlers before adding our own
|
// Execute the other handlers before adding our own
|
||||||
ctx.fireChannelRead(msg);
|
ctx.fireChannelRead(msg);
|
||||||
channel.pipeline().addLast(initProtocol);
|
channel.pipeline().addLast(initProtocol);
|
||||||
@ -97,11 +102,14 @@ public class NettyProtocolInjector implements ChannelListener {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Insert ProtocolLib's connection interceptor
|
// Insert ProtocolLib's connection interceptor
|
||||||
bootstrapField = getBootstrapField(serverConnection);
|
bootstrapFields = getBootstrapFields(serverConnection);
|
||||||
bootstrapField.setValue(new BootstrapList(
|
|
||||||
(List<ChannelFuture>) bootstrapField.getValue(), connectionHandler
|
|
||||||
));
|
|
||||||
|
|
||||||
|
for (VolatileField field : bootstrapFields) {
|
||||||
|
field.setValue(new BootstrapList(
|
||||||
|
(List<Object>) field.getValue(), connectionHandler
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
injected = true;
|
injected = true;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} 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.
|
* Inject our packet handling into a specific player.
|
||||||
* @param player
|
* @param player
|
||||||
@ -117,23 +130,20 @@ public class NettyProtocolInjector implements ChannelListener {
|
|||||||
ChannelInjector.fromPlayer(player, this).inject();
|
ChannelInjector.fromPlayer(player, this).inject();
|
||||||
}
|
}
|
||||||
|
|
||||||
private VolatileField getBootstrapField(Object serverConnection) {
|
private List<VolatileField> getBootstrapFields(Object serverConnection) {
|
||||||
VolatileField firstVolatile = null;
|
List<VolatileField> result = Lists.newArrayList();
|
||||||
|
|
||||||
|
// Find and (possibly) proxy every list
|
||||||
for (Field field : FuzzyReflection.fromObject(serverConnection, true).getFieldListByType(List.class)) {
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
List<Object> list = (List<Object>) currentVolatile.getValue();
|
List<Object> list = (List<Object>) volatileField.getValue();
|
||||||
|
|
||||||
// Also save the first list
|
if (list.size() == 0 || list.get(0) instanceof ChannelFuture) {
|
||||||
if (firstVolatile == null) {
|
result.add(volatileField);
|
||||||
firstVolatile = currentVolatile;
|
|
||||||
}
|
|
||||||
if (list.size() > 0 && list.get(0) instanceof ChannelFuture) {
|
|
||||||
return currentVolatile;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return firstVolatile;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,22 +152,21 @@ public class NettyProtocolInjector implements ChannelListener {
|
|||||||
public synchronized void close() {
|
public synchronized void close() {
|
||||||
if (!closed) {
|
if (!closed) {
|
||||||
closed = true;
|
closed = true;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
for (VolatileField field : bootstrapFields) {
|
||||||
List<Object> bootstraps = (List<Object>) bootstrapField.getValue();
|
Object value = field.getValue();
|
||||||
|
|
||||||
// Remember to close all the bootstraps
|
// Undo the processed channels, if any
|
||||||
for (Object value : bootstraps) {
|
|
||||||
if (value instanceof BootstrapList) {
|
if (value instanceof BootstrapList) {
|
||||||
((BootstrapList) value).close();
|
((BootstrapList) value).close();
|
||||||
}
|
}
|
||||||
|
field.revertValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uninject all the players
|
// Uninject all the players
|
||||||
for (Player player : Bukkit.getServer().getOnlinePlayers()) {
|
for (Player player : Bukkit.getServer().getOnlinePlayers()) {
|
||||||
ChannelInjector.fromPlayer(player, this).close();
|
ChannelInjector.fromPlayer(player, this).close();
|
||||||
}
|
}
|
||||||
bootstrapField.revertValue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,12 +270,14 @@ public class NettyProtocolInjector implements ChannelListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendServerPacket(Player reciever, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
|
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
|
@Override
|
||||||
public void recieveClientPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
|
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
|
@Override
|
||||||
@ -296,7 +307,8 @@ public class NettyProtocolInjector implements ChannelListener {
|
|||||||
@Override
|
@Override
|
||||||
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
|
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
|
||||||
NetworkMarker marker = buffered != null ? new NetworkMarker(ConnectionSide.CLIENT_SIDE, buffered) : null;
|
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);
|
return packetReceived(packet, client, marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,23 +30,17 @@ import net.sf.cglib.proxy.NoOp;
|
|||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.entity.Player;
|
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.reflect.FieldAccessException;
|
||||||
|
import com.comphenix.protocol.utility.ChatExtensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create fake player instances that represents pre-authenticated clients.
|
* Create fake player instances that represents pre-authenticated clients.
|
||||||
*/
|
*/
|
||||||
public class TemporaryPlayerFactory {
|
public class TemporaryPlayerFactory {
|
||||||
// Helpful constructors
|
|
||||||
private final PacketConstructor chatPacket;
|
|
||||||
|
|
||||||
// Prevent too many class creations
|
// Prevent too many class creations
|
||||||
private static CallbackFilter callbackFilter;
|
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.
|
* Retrieve the injector from a given player if it contains one.
|
||||||
* @param player - the player that may contain a reference to a player injector.
|
* @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.
|
* @throws FieldAccessException If we were unable to construct the message packet.
|
||||||
*/
|
*/
|
||||||
private Object sendMessage(SocketInjector injector, String message) throws InvocationTargetException, FieldAccessException {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import java.lang.reflect.Constructor;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
@ -29,9 +30,11 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import net.minecraft.util.com.google.common.base.Joiner;
|
||||||
import net.minecraft.util.com.google.common.collect.Sets;
|
import net.minecraft.util.com.google.common.collect.Sets;
|
||||||
|
|
||||||
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
|
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.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
@ -135,10 +138,22 @@ public class FuzzyReflection {
|
|||||||
*/
|
*/
|
||||||
public static FieldAccessor getFieldAccessor(Class<?> instanceClass, Class<?> fieldClass, boolean forceAccess) {
|
public static FieldAccessor getFieldAccessor(Class<?> instanceClass, Class<?> fieldClass, boolean forceAccess) {
|
||||||
// Get a field accessor
|
// Get a field accessor
|
||||||
Field field = FuzzyReflection.fromObject(instanceClass, forceAccess).getFieldByType(null, fieldClass);
|
Field field = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldByType(null, fieldClass);
|
||||||
return getFieldAccessor(field);
|
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.
|
* Retrieve a field accessor from a given field that uses unchecked exceptions.
|
||||||
* @param field - the field.
|
* @param field - the field.
|
||||||
@ -186,9 +201,30 @@ public class FuzzyReflection {
|
|||||||
* @return The method accessor.
|
* @return The method accessor.
|
||||||
*/
|
*/
|
||||||
public static MethodAccessor getMethodAccessor(Class<?> instanceClass, String name, Class<?>... parameters) {
|
public static MethodAccessor getMethodAccessor(Class<?> instanceClass, String name, Class<?>... parameters) {
|
||||||
Method method = MethodUtils.getAccessibleMethod(instanceClass, name, parameters);
|
return getMethodAccessor(instanceClass, instanceClass, name, parameters);
|
||||||
method.setAccessible(true);
|
}
|
||||||
return getMethodAccessor(method);
|
|
||||||
|
// 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;
|
Field field = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
method = getMethodByParameters("getInstance", source.getClass(), new Class<?>[0]);
|
method = getMethod(
|
||||||
|
FuzzyMethodContract.newBuilder().
|
||||||
|
parameterCount(0).
|
||||||
|
returnDerivedOf(source).
|
||||||
|
requireModifier(Modifier.STATIC).
|
||||||
|
build()
|
||||||
|
);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// Try getting the field instead
|
// Try getting the field instead
|
||||||
// Note that this will throw an exception if not found
|
// Note that this will throw an exception if not found
|
||||||
|
@ -122,7 +122,6 @@ public class VolatileField {
|
|||||||
* @param newValue - new field value.
|
* @param newValue - new field value.
|
||||||
*/
|
*/
|
||||||
public void setValue(Object newValue) {
|
public void setValue(Object newValue) {
|
||||||
|
|
||||||
// Remember to safe the previous value
|
// Remember to safe the previous value
|
||||||
ensureLoaded();
|
ensureLoaded();
|
||||||
|
|
||||||
@ -132,7 +131,20 @@ public class VolatileField {
|
|||||||
currentSet = true;
|
currentSet = true;
|
||||||
|
|
||||||
} catch (IllegalAccessException e) {
|
} 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ package com.comphenix.protocol.utility;
|
|||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -27,12 +26,14 @@ import org.bukkit.Bukkit;
|
|||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.ProtocolManager;
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.injector.PacketConstructor;
|
import com.comphenix.protocol.injector.PacketConstructor;
|
||||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
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.FuzzyMatchers;
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
@ -45,12 +46,14 @@ import com.google.common.collect.Iterables;
|
|||||||
*/
|
*/
|
||||||
public class ChatExtensions {
|
public class ChatExtensions {
|
||||||
// Used to sent chat messages
|
// Used to sent chat messages
|
||||||
private PacketConstructor chatConstructor;
|
|
||||||
private ProtocolManager manager;
|
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
|
// Whether or not we have to use the post-1.6.1 chat format
|
||||||
private static Constructor<?> jsonConstructor = getJsonFormatConstructor();
|
private static volatile Constructor<?> jsonConstructor = getJsonFormatConstructor();
|
||||||
private static Method messageFactory;
|
private static volatile MethodAccessor messageFactory;
|
||||||
|
|
||||||
public ChatExtensions(ProtocolManager manager) {
|
public ChatExtensions(ProtocolManager manager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
@ -83,63 +86,64 @@ public class ChatExtensions {
|
|||||||
* @throws InvocationTargetException If we were unable to send the message.
|
* @throws InvocationTargetException If we were unable to send the message.
|
||||||
*/
|
*/
|
||||||
private void sendMessageSilently(Player player, String message) throws InvocationTargetException {
|
private void sendMessageSilently(Player player, String message) throws InvocationTargetException {
|
||||||
if (jsonConstructor != null) {
|
try {
|
||||||
sendMessageAsJson(player, message);
|
for (PacketContainer packet : createChatPackets(message)) {
|
||||||
} else {
|
manager.sendServerPacket(player, packet, false);
|
||||||
sendMessageAsString(player, message);
|
}
|
||||||
|
} catch (FieldAccessException e) {
|
||||||
|
throw new InvocationTargetException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message using the new JSON format in 1.6.1.
|
* Construct chat packet to send in order to display a given message.
|
||||||
* @param player - the player to send it to.
|
|
||||||
* @param message - the message to send.
|
* @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 {
|
public static PacketContainer[] createChatPackets(String message) {
|
||||||
Object messageObject = null;
|
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) {
|
} else {
|
||||||
Class<?> messageClass = jsonConstructor.getParameterTypes()[0];
|
if (chatConstructor == null) {
|
||||||
chatConstructor = manager.createPacketConstructor(Packets.Server.CHAT, messageClass);
|
chatConstructor = PacketConstructor.DEFAULT.withPacket(PacketType.Play.Server.CHAT, new Object[] { message });
|
||||||
|
}
|
||||||
// Try one of the string constructors
|
// Minecraft 1.6.0 and earlier
|
||||||
messageFactory = FuzzyReflection.fromClass(messageClass).getMethod(
|
return new PacketContainer[] { chatConstructor.createPacket(message) };
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +214,7 @@ public class ChatExtensions {
|
|||||||
* @return A constructor for JSON-based packets.
|
* @return A constructor for JSON-based packets.
|
||||||
*/
|
*/
|
||||||
static Constructor<?> getJsonFormatConstructor() {
|
static Constructor<?> getJsonFormatConstructor() {
|
||||||
Class<?> chatPacket = PacketRegistry.getPacketClassFromID(3, true);
|
Class<?> chatPacket = PacketRegistry.getPacketClassFromType(PacketType.Play.Server.CHAT, true);
|
||||||
List<Constructor<?>> list = FuzzyReflection.fromClass(chatPacket).getConstructorList(
|
List<Constructor<?>> list = FuzzyReflection.fromClass(chatPacket).getConstructorList(
|
||||||
FuzzyMethodContract.newBuilder().
|
FuzzyMethodContract.newBuilder().
|
||||||
parameterCount(1).
|
parameterCount(1).
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
package com.comphenix.protocol.utility;
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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.ByteBuf;
|
||||||
import net.minecraft.util.io.netty.buffer.UnpooledByteBufAllocator;
|
import net.minecraft.util.io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
||||||
|
@ -1366,6 +1366,14 @@ public class MinecraftReflection {
|
|||||||
public static Class<?> getCraftEntityClass() {
|
public static Class<?> getCraftEntityClass() {
|
||||||
return getCraftBukkitClass("entity.CraftEntity");
|
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.
|
* Retrieve a CraftItemStack from a given ItemStack.
|
||||||
|
@ -7,7 +7,6 @@ import java.io.DataInputStream;
|
|||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
@ -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.Block;
|
||||||
import net.minecraft.server.v1_7_R1.Item;
|
import net.minecraft.server.v1_7_R1.Item;
|
||||||
import net.minecraft.server.v1_7_R1.RegistryMaterials;
|
|
||||||
import net.minecraft.server.v1_7_R1.StatisticList;
|
import net.minecraft.server.v1_7_R1.StatisticList;
|
||||||
|
|
||||||
// Will have to be updated for every version though
|
// Will have to be updated for every version though
|
||||||
|
Loading…
Reference in New Issue
Block a user