mirror of
https://github.com/ViaVersion/ViaVersion.git
synced 2024-11-29 13:36:16 +01:00
This is a huge commit, adds ProtocolLib support. (To the best extent I can):
Channels now work as proxies for the minecraft encoder and decoder, this allows better compatibility when transforming packets. ConnectionInfo now holds an activate state to deactivate. Now only 2 handlers We now use info.getChannel().pipeline().writeAndFlush to ensure we catch it ourselves. Fix EntityMetadata from last commit so it sends empty metadata instead of cancelling whole packet. Warn if they reload and don't reinject.
This commit is contained in:
parent
b5b0117fa9
commit
318db73f3c
@ -9,11 +9,12 @@ import java.util.UUID;
|
||||
|
||||
public class ConnectionInfo {
|
||||
private final SocketChannel channel;
|
||||
private int protocol = 0;
|
||||
private State state = State.HANDSHAKE;
|
||||
private int compression = 0;
|
||||
private Object lastPacket;
|
||||
private java.util.UUID UUID;
|
||||
private State state = State.HANDSHAKE;
|
||||
private int protocol = 0;
|
||||
private int compression = 0;
|
||||
private boolean active = true;
|
||||
|
||||
public ConnectionInfo(SocketChannel socketChannel) {
|
||||
this.channel = socketChannel;
|
||||
@ -66,4 +67,12 @@ public class ConnectionInfo {
|
||||
public SocketChannel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
}
|
||||
|
@ -34,14 +34,17 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
ViaVersion.setInstance(this);
|
||||
System.out.println("ViaVersion enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
|
||||
if(System.getProperty("ViaVersion") != null){
|
||||
getLogger().severe("ViaVersion is already loaded, we don't support reloads. Please reboot if you wish to update.");
|
||||
return;
|
||||
}
|
||||
|
||||
getLogger().info("ViaVersion enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
|
||||
try {
|
||||
injectPacketHandler();
|
||||
System.setProperty("ViaVersion", getDescription().getVersion());
|
||||
} catch (Exception e) {
|
||||
if(Bukkit.getPluginManager().getPlugin("ProtocolLib") != null){
|
||||
System.out.println("This plugin is not compatible with protocol lib.");
|
||||
}
|
||||
System.out.println("Unable to inject handlers, are you on 1.8? ");
|
||||
getLogger().severe("Unable to inject handlers, are you on 1.8? ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
Bukkit.getPluginManager().registerEvents(new Listener() {
|
||||
@ -59,7 +62,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
|
||||
List<ChannelFuture> futures = ReflectionUtil.get(connection, "g", List.class);
|
||||
if (futures.size() == 0) {
|
||||
throw new Exception("Could not find server to inject (late bind?)");
|
||||
throw new Exception("Could not find server to inject (Please ensure late-bind in your spigot.yml is false)");
|
||||
}
|
||||
|
||||
for (ChannelFuture future : futures) {
|
||||
|
@ -0,0 +1,48 @@
|
||||
package us.myles.ViaVersion.handlers;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import us.myles.ViaVersion.CancelException;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.transformers.IncomingTransformer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ViaDecodeHandler extends ByteToMessageDecoder {
|
||||
private final IncomingTransformer incomingTransformer;
|
||||
private final ByteToMessageDecoder minecraftDecoder;
|
||||
private final ConnectionInfo info;
|
||||
|
||||
public ViaDecodeHandler(ConnectionInfo info, ByteToMessageDecoder minecraftDecoder) {
|
||||
this.info = info;
|
||||
this.minecraftDecoder = minecraftDecoder;
|
||||
this.incomingTransformer = new IncomingTransformer(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception {
|
||||
// use transformers
|
||||
if(bytebuf.readableBytes() > 0) {
|
||||
if(info.isActive()) {
|
||||
int id = PacketUtil.readVarInt(bytebuf);
|
||||
// Transform
|
||||
ByteBuf newPacket = ctx.alloc().buffer();
|
||||
try {
|
||||
incomingTransformer.transform(id, bytebuf, newPacket);
|
||||
bytebuf = newPacket;
|
||||
} catch (CancelException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// call minecraft decoder
|
||||
list.addAll(PacketUtil.callDecode(this.minecraftDecoder, ctx, bytebuf));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package us.myles.ViaVersion.handlers;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import us.myles.ViaVersion.CancelException;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.transformers.OutgoingTransformer;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
public class ViaEncodeHandler extends MessageToByteEncoder {
|
||||
private final ConnectionInfo info;
|
||||
private final MessageToByteEncoder minecraftEncoder;
|
||||
private final OutgoingTransformer outgoingTransformer;
|
||||
|
||||
public ViaEncodeHandler(ConnectionInfo info, MessageToByteEncoder minecraftEncoder) {
|
||||
this.info = info;
|
||||
this.minecraftEncoder = minecraftEncoder;
|
||||
this.outgoingTransformer = new OutgoingTransformer(info);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Object o, ByteBuf bytebuf) throws Exception {
|
||||
// handle the packet type
|
||||
if (o == null) return;
|
||||
if (!(o instanceof ByteBuf)) {
|
||||
info.setLastPacket(o);
|
||||
/* This transformer is more for fixing issues which we find hard at packet level :) */
|
||||
if (o.getClass().getName().endsWith("PacketPlayOutMapChunkBulk")) {
|
||||
int[] locX = ReflectionUtil.get(o, "a", int[].class);
|
||||
int[] locZ = ReflectionUtil.get(o, "b", int[].class);
|
||||
|
||||
Object world = ReflectionUtil.get(o, "world", ReflectionUtil.nms("World"));
|
||||
Class<?> mapChunk = ReflectionUtil.nms("PacketPlayOutMapChunk");
|
||||
Constructor constructor = mapChunk.getDeclaredConstructor(ReflectionUtil.nms("Chunk"), boolean.class, int.class);
|
||||
for (int i = 0; i < locX.length; i++) {
|
||||
int x = locX[i];
|
||||
int z = locZ[i];
|
||||
// world invoke function
|
||||
Object chunk = ReflectionUtil.nms("World").getDeclaredMethod("getChunkAt", int.class, int.class).invoke(world, x, z);
|
||||
Object packet = constructor.newInstance(chunk, true, 65535);
|
||||
ctx.pipeline().writeAndFlush(packet);
|
||||
}
|
||||
bytebuf.clear();
|
||||
return;
|
||||
}
|
||||
// call minecraft encoder
|
||||
PacketUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf);
|
||||
}
|
||||
if (bytebuf.readableBytes() == 0) {
|
||||
return;
|
||||
}
|
||||
if(info.isActive()) {
|
||||
int id = PacketUtil.readVarInt(bytebuf);
|
||||
// Transform
|
||||
ByteBuf oldPacket = bytebuf.copy();
|
||||
bytebuf.clear();
|
||||
try {
|
||||
outgoingTransformer.transform(id, oldPacket, bytebuf);
|
||||
} catch (CancelException e) {
|
||||
return;
|
||||
} finally {
|
||||
oldPacket.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package us.myles.ViaVersion.handlers;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import us.myles.ViaVersion.CancelException;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.transformers.IncomingTransformer;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public class ViaInboundHandler extends ChannelInboundHandlerAdapter {
|
||||
private final IncomingTransformer incomingTransformer;
|
||||
|
||||
public ViaInboundHandler(ConnectionInfo info) {
|
||||
this.incomingTransformer = new IncomingTransformer(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
boolean compression = ctx.pipeline().get("compress") != null;
|
||||
|
||||
if (msg instanceof ByteBuf) {
|
||||
ByteBuf bytebuf = (ByteBuf) msg;
|
||||
if (compression) {
|
||||
// decompress :)
|
||||
bytebuf = PacketUtil.decompress(ctx, bytebuf);
|
||||
}
|
||||
int id = PacketUtil.readVarInt(bytebuf);
|
||||
// Transform
|
||||
ByteBuf newPacket = ctx.alloc().buffer();
|
||||
try {
|
||||
incomingTransformer.transform(id, bytebuf, newPacket);
|
||||
} catch (CancelException e) {
|
||||
return;
|
||||
} finally {
|
||||
bytebuf.release();
|
||||
}
|
||||
if (compression) {
|
||||
// recompress :)
|
||||
newPacket = PacketUtil.compress(ctx, newPacket);
|
||||
}
|
||||
msg = newPacket;
|
||||
}
|
||||
super.channelRead(ctx, msg);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
package us.myles.ViaVersion.handlers;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.*;
|
||||
import us.myles.ViaVersion.CancelException;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.transformers.OutgoingTransformer;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public class ViaOutboundHandler extends ChannelOutboundHandlerAdapter {
|
||||
private final OutgoingTransformer outgoingTransformer;
|
||||
private final ConnectionInfo info;
|
||||
|
||||
public ViaOutboundHandler(ConnectionInfo info) {
|
||||
this.info = info;
|
||||
this.outgoingTransformer = new OutgoingTransformer(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise channelPromise) throws Exception {
|
||||
try {
|
||||
if (channelPromise.isDone()) return; // don't break any <3s
|
||||
boolean compression = ctx.pipeline().get("compress") != null;
|
||||
if (msg instanceof ByteBuf) {
|
||||
ByteBuf bytebuf = (ByteBuf) msg;
|
||||
if (compression) {
|
||||
// decompress :)
|
||||
bytebuf = PacketUtil.decompress(ctx, bytebuf);
|
||||
}
|
||||
int id = PacketUtil.readVarInt(bytebuf);
|
||||
// Transform
|
||||
ByteBuf newPacket = ctx.alloc().buffer();
|
||||
try {
|
||||
outgoingTransformer.transform(id, bytebuf, newPacket);
|
||||
} catch (CancelException e) {
|
||||
return;
|
||||
} finally {
|
||||
bytebuf.release();
|
||||
}
|
||||
if (compression) {
|
||||
// recompress :)
|
||||
newPacket = PacketUtil.compress(ctx, newPacket);
|
||||
}
|
||||
msg = newPacket;
|
||||
}
|
||||
super.write(ctx, msg, channelPromise);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package us.myles.ViaVersion.handlers;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.*;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public class ViaOutboundPacketHandler extends ChannelOutboundHandlerAdapter {
|
||||
private final ConnectionInfo info;
|
||||
|
||||
public ViaOutboundPacketHandler(ConnectionInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext channelHandlerContext, Object o, ChannelPromise channelPromise) throws Exception {
|
||||
if (!(o instanceof ByteBuf)) {
|
||||
info.setLastPacket(o);
|
||||
/* This transformer is more for fixing issues which we find hard at byte level :) */
|
||||
if (o.getClass().getName().endsWith("PacketPlayOutMapChunkBulk")) {
|
||||
int[] locX = ReflectionUtil.get(o, "a", int[].class);
|
||||
int[] locZ = ReflectionUtil.get(o, "b", int[].class);
|
||||
|
||||
Object world = ReflectionUtil.get(o, "world", ReflectionUtil.nms("World"));
|
||||
Class<?> mapChunk = ReflectionUtil.nms("PacketPlayOutMapChunk");
|
||||
Constructor constructor = mapChunk.getDeclaredConstructor(ReflectionUtil.nms("Chunk"), boolean.class, int.class);
|
||||
for (int i = 0; i < locX.length; i++) {
|
||||
int x = locX[i];
|
||||
int z = locZ[i];
|
||||
// world invoke function
|
||||
Object chunk = ReflectionUtil.nms("World").getDeclaredMethod("getChunkAt", int.class, int.class).invoke(world, x, z);
|
||||
Object packet = constructor.newInstance(chunk, true, 65535);
|
||||
channelHandlerContext.write(packet);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.write(channelHandlerContext, o, channelPromise);
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@ package us.myles.ViaVersion.handlers;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@ -27,12 +29,10 @@ public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
|
||||
// Add originals
|
||||
this.method.invoke(this.oldInit, socketChannel);
|
||||
// Add our transformers
|
||||
ViaInboundHandler inbound = new ViaInboundHandler(info);
|
||||
ViaOutboundHandler outbound = new ViaOutboundHandler(info);
|
||||
ViaOutboundPacketHandler outbound2 = new ViaOutboundPacketHandler(info);
|
||||
socketChannel.pipeline().addBefore("decoder", "via_incoming", inbound);
|
||||
socketChannel.pipeline().addBefore("packet_handler", "via_outgoing2", outbound2);
|
||||
socketChannel.pipeline().addBefore("encoder", "via_outgoing", outbound);
|
||||
ViaEncodeHandler encoder = new ViaEncodeHandler(info, (MessageToByteEncoder) socketChannel.pipeline().get("encoder"));
|
||||
ViaDecodeHandler decoder = new ViaDecodeHandler(info, (ByteToMessageDecoder) socketChannel.pipeline().get("decoder"));
|
||||
|
||||
socketChannel.pipeline().replace("encoder", "encoder", encoder);
|
||||
socketChannel.pipeline().replace("decoder", "decoder", decoder);
|
||||
}
|
||||
}
|
||||
|
@ -45,9 +45,7 @@ public class IncomingTransformer {
|
||||
|
||||
if (protVer <= 102) {
|
||||
// not 1.9, remove pipes
|
||||
info.getChannel().pipeline().remove("via_incoming");
|
||||
info.getChannel().pipeline().remove("via_outgoing");
|
||||
info.getChannel().pipeline().remove("via_outgoing2");
|
||||
info.setActive(false);
|
||||
}
|
||||
String serverAddress = PacketUtil.readString(input);
|
||||
PacketUtil.writeString(serverAddress, output);
|
||||
@ -105,7 +103,7 @@ public class IncomingTransformer {
|
||||
try {
|
||||
Class<?> setSlot = ReflectionUtil.nms("PacketPlayOutSetSlot");
|
||||
Object setSlotPacket = setSlot.getConstructors()[1].newInstance(windowID, slot, null);
|
||||
info.getChannel().writeAndFlush(setSlotPacket); // slot is empty
|
||||
info.getChannel().pipeline().writeAndFlush(setSlotPacket); // slot is empty
|
||||
slot = -999; // we're evil, they'll throw item on the ground
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -290,7 +290,6 @@ public class OutgoingTransformer {
|
||||
int id = PacketUtil.readVarInt(input);
|
||||
clientEntityTypes.put(id, EntityType.EXPERIENCE_ORB);
|
||||
PacketUtil.writeVarInt(id, output);
|
||||
|
||||
double x = input.readInt();
|
||||
output.writeDouble(x / 32D);
|
||||
double y = input.readInt();
|
||||
@ -309,7 +308,6 @@ public class OutgoingTransformer {
|
||||
PacketUtil.writeVarInt(id, output);
|
||||
|
||||
PacketUtil.writeUUID(getUUID(id), output);
|
||||
|
||||
String title = PacketUtil.readString(input);
|
||||
PacketUtil.writeString(title, output);
|
||||
|
||||
@ -533,7 +531,8 @@ public class OutgoingTransformer {
|
||||
EntityType type = clientEntityTypes.get(entityID);
|
||||
if (type == null) {
|
||||
System.out.println("Unable to get entity for ID: " + entityID);
|
||||
throw new CancelException();
|
||||
output.writeByte(255);
|
||||
return;
|
||||
}
|
||||
if (dw != null) {
|
||||
short id = -1;
|
||||
|
@ -42,29 +42,38 @@ public class PacketUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static ByteBuf decompress(ChannelHandlerContext ctx, ByteBuf msg) {
|
||||
ByteToMessageDecoder x = (ByteToMessageDecoder) ctx.pipeline().get("decompress");
|
||||
public static List<Object> callDecode(ByteToMessageDecoder decoder, ChannelHandlerContext ctx, Object input) {
|
||||
List<Object> output = new ArrayList<Object>();
|
||||
try {
|
||||
PacketUtil.DECODE_METHOD.invoke(x, ctx, msg, output);
|
||||
PacketUtil.DECODE_METHOD.invoke(decoder, ctx, input, output);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public static void callEncode(MessageToByteEncoder encoder, ChannelHandlerContext ctx, Object msg, ByteBuf output) {
|
||||
try {
|
||||
PacketUtil.ENCODE_METHOD.invoke(encoder, ctx, msg, output);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static ByteBuf decompress(ChannelHandlerContext ctx, ByteBuf msg) {
|
||||
ByteToMessageDecoder x = (ByteToMessageDecoder) ctx.pipeline().get("decompress");
|
||||
List<Object> output = callDecode(x, ctx, msg);
|
||||
return output.size() == 0 ? null : (ByteBuf) output.get(0);
|
||||
}
|
||||
|
||||
public static ByteBuf compress(ChannelHandlerContext ctx, ByteBuf msg) {
|
||||
MessageToByteEncoder x = (MessageToByteEncoder) ctx.pipeline().get("compress");
|
||||
ByteBuf output = ctx.alloc().buffer();
|
||||
try {
|
||||
PacketUtil.ENCODE_METHOD.invoke(x, ctx, msg, output);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
callEncode(x, ctx, msg, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
@ -185,9 +194,9 @@ public class PacketUtil {
|
||||
}
|
||||
|
||||
public static void writeVarIntArray(List<Integer> integers, ByteBuf output) {
|
||||
writeVarInt(integers.size(),output);
|
||||
for (Integer i : integers){
|
||||
writeVarInt(i,output);
|
||||
writeVarInt(integers.size(), output);
|
||||
for (Integer i : integers) {
|
||||
writeVarInt(i, output);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
name: ViaVersion
|
||||
main: us.myles.ViaVersion.ViaVersionPlugin
|
||||
author: _MylesC
|
||||
version: 0.3.7
|
||||
version: 0.3.7
|
||||
load: startup
|
||||
loadbefore: [ProtocolLib, ProxyPipe]
|
Loading…
Reference in New Issue
Block a user