Merge pull request #1723 from creeper123123321/backendinjection

Velocity Backend injection / Velocity 1.1.0
This commit is contained in:
Myles 2020-11-02 17:29:31 +00:00 committed by GitHub
commit 9821cb1b16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 267 additions and 305 deletions

View File

@ -61,7 +61,7 @@ public class BukkitEncodeHandler extends MessageToByteEncoder implements ViaHand
@Override
public void transform(ByteBuf bytebuf) throws Exception {
info.checkOutgoingPacket();
if (!info.checkOutgoingPacket()) throw CancelEncoderException.generate(null);
if (!info.shouldTransformPacket()) return;
info.transformOutgoing(bytebuf, CancelEncoderException::generate);
}

View File

@ -22,7 +22,7 @@ public class BungeeEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
@Override
protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
info.checkOutgoingPacket();
if (!info.checkOutgoingPacket()) throw CancelEncoderException.generate(null);
if (!info.shouldTransformPacket()) {
out.add(bytebuf.retain());
return;

View File

@ -12,14 +12,6 @@
<artifactId>viaversion-common</artifactId>
<dependencies>
<!-- Snake YAML -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.18</version>
<scope>compile</scope> <!-- Velocity doesn't have snakeyaml -->
</dependency>
<!-- FastUtil for performance increases -->
<dependency>
<groupId>it.unimi.dsi</groupId>

View File

@ -91,7 +91,7 @@ public class ViaManager {
// Check if there are any pipes to this version
if (ProtocolRegistry.SERVER_PROTOCOL != -1) {
platform.getLogger().info("ViaVersion detected server version: " + ProtocolVersion.getProtocol(ProtocolRegistry.SERVER_PROTOCOL));
if (!ProtocolRegistry.isWorkingPipe()) {
if (!ProtocolRegistry.isWorkingPipe() && !platform.isProxy()) {
platform.getLogger().warning("ViaVersion does not have any compatible versions for this server version!");
platform.getLogger().warning("Please remember that ViaVersion only adds support for versions newer than the server version.");
platform.getLogger().warning("If you need support for older versions you may need to use one or more ViaVersion addons too.");

View File

@ -3,7 +3,6 @@ package us.myles.ViaVersion.api.data;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import net.md_5.bungee.api.ChatColor;
import org.jetbrains.annotations.Nullable;
@ -18,7 +17,6 @@ import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.util.PipelineUtil;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
@ -27,8 +25,9 @@ public class UserConnection {
private static final AtomicLong IDS = new AtomicLong();
private final long id = IDS.incrementAndGet();
private final Channel channel;
private ProtocolInfo protocolInfo;
private final boolean clientSide;
Map<Class, StoredObject> storedObjects = new ConcurrentHashMap<>();
private ProtocolInfo protocolInfo;
private boolean active = true;
private boolean pendingDisconnect;
private Object lastPacket;
@ -42,8 +41,23 @@ public class UserConnection {
private int secondsObserved;
private int warnings;
public UserConnection(@Nullable Channel channel) {
/**
* Creates an UserConnection. When it's a client-side connection, some method behaviors are modified.
*
* @param channel netty channel.
* @param clientSide true if it's a client-side connection
*/
public UserConnection(@Nullable Channel channel, boolean clientSide) {
this.channel = channel;
this.clientSide = clientSide;
}
/**
* @param channel
* @see #UserConnection(Channel, boolean)
*/
public UserConnection(@Nullable Channel channel) {
this(channel, false);
}
/**
@ -91,12 +105,24 @@ public class UserConnection {
* @param packet The raw packet to send
* @param currentThread Should it run in the same thread
*/
public void sendRawPacket(ByteBuf packet, boolean currentThread) {
ChannelHandler handler = channel.pipeline().get(Via.getManager().getInjector().getEncoderName());
if (currentThread) {
channel.pipeline().context(handler).writeAndFlush(packet);
public void sendRawPacket(final ByteBuf packet, boolean currentThread) {
Runnable act;
if (clientSide) {
// We'll just assume that Via decoder isn't wrapping the original decoder
act = () -> getChannel().pipeline()
.context(Via.getManager().getInjector().getDecoderName()).fireChannelRead(packet);
} else {
channel.eventLoop().submit(() -> channel.pipeline().context(handler).writeAndFlush(packet));
act = () -> channel.pipeline().context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet);
}
if (currentThread) {
act.run();
} else {
try {
channel.eventLoop().submit(act);
} catch (Throwable e) {
packet.release(); // Couldn't schedule
e.printStackTrace();
}
}
}
@ -106,9 +132,22 @@ public class UserConnection {
* @param packet The raw packet to send
* @return ChannelFuture of the packet being sent
*/
public ChannelFuture sendRawPacketFuture(ByteBuf packet) {
ChannelHandler handler = channel.pipeline().get(Via.getManager().getInjector().getEncoderName());
return channel.pipeline().context(handler).writeAndFlush(packet);
public ChannelFuture sendRawPacketFuture(final ByteBuf packet) {
if (clientSide) {
return sendRawPacketFutureClientSide(packet);
} else {
return sendRawPacketFutureServerSide(packet);
}
}
private ChannelFuture sendRawPacketFutureServerSide(final ByteBuf packet) {
return channel.pipeline().context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet);
}
private ChannelFuture sendRawPacketFutureClientSide(final ByteBuf packet) {
// Assume that decoder isn't wrapping
getChannel().pipeline().context(Via.getManager().getInjector().getDecoderName()).fireChannelRead(packet);
return getChannel().newSucceededFuture();
}
/**
@ -156,6 +195,7 @@ public class UserConnection {
* @see #incrementReceived()
*/
public boolean exceedsMaxPPS() {
if (clientSide) return false; // Don't apply PPS limiting for client-side
ViaVersionConfig conf = Via.getConfig();
// Max PPS Checker
if (conf.getMaxPPS() > 0) {
@ -195,14 +235,8 @@ public class UserConnection {
if (!channel.isOpen() || pendingDisconnect) return;
pendingDisconnect = true;
UUID uuid = protocolInfo.getUuid();
if (uuid == null) {
channel.close(); // Just disconnect, we don't know what the connection is
return;
}
Via.getPlatform().runSync(() -> {
if (!Via.getPlatform().kickPlayer(uuid, ChatColor.translateAlternateColorCodes('&', reason))) {
if (!Via.getPlatform().disconnect(this, ChatColor.translateAlternateColorCodes('&', reason))) {
channel.close(); // =)
}
});
@ -214,9 +248,20 @@ public class UserConnection {
* @param packet Raw packet to be sent
* @param currentThread If {@code true} executes immediately, {@code false} submits a task to EventLoop
*/
public void sendRawPacketToServer(ByteBuf packet, boolean currentThread) {
ByteBuf buf = packet.alloc().buffer();
public void sendRawPacketToServer(final ByteBuf packet, boolean currentThread) {
if (clientSide) {
sendRawPacketToServerClientSide(packet, currentThread);
} else {
sendRawPacketToServerServerSide(packet, currentThread);
}
}
private void sendRawPacketToServerServerSide(final ByteBuf packet, boolean currentThread) {
final ByteBuf buf = packet.alloc().buffer();
try {
// We'll use passing through because there are some encoder wrappers
ChannelHandlerContext context = PipelineUtil
.getPreviousContext(Via.getManager().getInjector().getDecoderName(), channel.pipeline());
try {
Type.VAR_INT.writePrimitive(buf, PacketWrapper.PASSTHROUGH_ID);
} catch (Exception e) {
@ -224,23 +269,18 @@ public class UserConnection {
Via.getPlatform().getLogger().warning("Type.VAR_INT.write thrown an exception: " + e);
}
buf.writeBytes(packet);
ChannelHandlerContext context = PipelineUtil
.getPreviousContext(Via.getManager().getInjector().getDecoderName(), channel.pipeline());
if (currentThread) {
Runnable act = () -> {
if (context != null) {
context.fireChannelRead(buf);
} else {
channel.pipeline().fireChannelRead(buf);
}
};
if (currentThread) {
act.run();
} else {
try {
channel.eventLoop().submit(() -> {
if (context != null) {
context.fireChannelRead(buf);
} else {
channel.pipeline().fireChannelRead(buf);
}
});
channel.eventLoop().submit(act);
} catch (Throwable t) {
// Couldn't schedule
buf.release();
@ -252,6 +292,21 @@ public class UserConnection {
}
}
private void sendRawPacketToServerClientSide(final ByteBuf packet, boolean currentThread) {
Runnable act = () -> getChannel().pipeline()
.context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet);
if (currentThread) {
act.run();
} else {
try {
getChannel().eventLoop().submit(act);
} catch (Throwable e) {
e.printStackTrace();
packet.release(); // Couldn't schedule
}
}
}
/**
* Sends a raw packet to the server. It will submit a task to EventLoop.
*
@ -262,11 +317,24 @@ public class UserConnection {
}
/**
* Monitors serverbound packets.
* Monitors incoming packets
*
* @return false if this packet should be cancelled
*/
public boolean checkIncomingPacket() {
if (clientSide) {
return checkClientBound();
} else {
return checkServerBound();
}
}
private boolean checkClientBound() {
incrementSent();
return true;
}
private boolean checkServerBound() {
// Ignore if pending disconnect
if (pendingDisconnect) return false;
// Increment received + Check PPS
@ -274,10 +342,16 @@ public class UserConnection {
}
/**
* Monitors clientbound packets.
* Monitors outgoing packets
*
* @return false if this packet should be cancelled
*/
public void checkOutgoingPacket() {
incrementSent();
public boolean checkOutgoingPacket() {
if (clientSide) {
return checkServerBound();
} else {
return checkClientBound();
}
}
/**
@ -290,7 +364,8 @@ public class UserConnection {
}
/**
* Transforms the clientbound packet contained in an outgoing ByteBuf.
* Transforms the outgoing packet contained in ByteBuf. When clientSide is true, this packet is considered
* serverbound.
*
* @param buf ByteBuf with packet id and packet contents
* @param cancelSupplier Function called with original CancelException for generating the Exception used when
@ -301,11 +376,12 @@ public class UserConnection {
*/
public void transformOutgoing(ByteBuf buf, Function<Throwable, Exception> cancelSupplier) throws Exception {
if (!buf.isReadable()) return;
transform(buf, Direction.OUTGOING, cancelSupplier);
transform(buf, clientSide ? Direction.INCOMING : Direction.OUTGOING, cancelSupplier);
}
/**
* Transforms the serverbound packet contained in an incoming ByteBuf.
* Transforms the incoming packet contained in ByteBuf. When clientSide is true, this packet is considered
* clientbound
*
* @param buf ByteBuf with packet id and packet contents
* @param cancelSupplier Function called with original CancelException for generating the Exception used when
@ -316,7 +392,7 @@ public class UserConnection {
*/
public void transformIncoming(ByteBuf buf, Function<Throwable, Exception> cancelSupplier) throws Exception {
if (!buf.isReadable()) return;
transform(buf, Direction.INCOMING, cancelSupplier);
transform(buf, clientSide ? Direction.OUTGOING : Direction.INCOMING, cancelSupplier);
}
private void transform(ByteBuf buf, Direction direction, Function<Throwable, Exception> cancelSupplier) throws Exception {
@ -459,4 +535,12 @@ public class UserConnection {
public int hashCode() {
return Long.hashCode(id);
}
public boolean isClientSide() {
return clientSide;
}
public boolean shouldApplyBlockProtocol() {
return !clientSide; // Don't apply protocol blocking on client-side
}
}

View File

@ -46,7 +46,7 @@ public class ViaConnectionManager {
* UUIDs can't be duplicate between frontend connections.
*/
public boolean isFrontEnd(UserConnection conn) {
return true;
return !conn.isClientSide();
}
/**

View File

@ -1,10 +1,13 @@
package us.myles.ViaVersion.api.platform;
import com.google.gson.JsonObject;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.ViaAPI;
import us.myles.ViaVersion.api.ViaVersionConfig;
import us.myles.ViaVersion.api.command.ViaCommandSender;
import us.myles.ViaVersion.api.configuration.ConfigurationProvider;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import java.io.File;
import java.util.UUID;
@ -121,6 +124,20 @@ public interface ViaPlatform<T> {
*/
boolean kickPlayer(UUID uuid, String message);
/**
* Disconnects an UserConnection for a reason
*
* @param connection The UserConnection
* @param message The message to kick them with
* @return True if it was successful
*/
default boolean disconnect(UserConnection connection, String message) {
if (connection.isClientSide()) return false;
UUID uuid = connection.get(ProtocolInfo.class).getUuid();
if (uuid == null) return false;
return kickPlayer(uuid, message);
}
/**
* Check if the plugin is enabled.
*

View File

@ -165,6 +165,7 @@ public class BaseProtocol1_7 extends SimpleProtocol {
int protocol = wrapper.user().getProtocolInfo().getProtocolVersion();
if (Via.getConfig().getBlockedProtocols().contains(protocol)) {
if (!wrapper.user().getChannel().isOpen()) return;
if (!wrapper.user().shouldApplyBlockProtocol()) return;
PacketWrapper disconnectPacket = new PacketWrapper(0x00, null, wrapper.user()); // Disconnect Packet
Protocol1_9To1_8.FIX_JSON.write(disconnectPacket, ChatColor.translateAlternateColorCodes('&', Via.getConfig().getBlockedDisconnectMsg()));

View File

@ -123,10 +123,6 @@
<pattern>javassist</pattern>
<shadedPattern>us.myles.viaversion.libs.javassist</shadedPattern>
</relocation>
<relocation>
<pattern>org.yaml.snakeyaml</pattern>
<shadedPattern>us.myles.viaversion.libs.snakeyaml</shadedPattern>
</relocation>
<relocation>
<pattern>net.md_5.bungee</pattern>
<shadedPattern>us.myles.viaversion.libs.bungeecordchat</shadedPattern>

14
pom.xml
View File

@ -70,6 +70,12 @@
<id>viaversion-repo</id>
<url>https://repo.viaversion.com</url>
</repository>
<!-- Mojang's Minecraft Libraries -->
<repository>
<id>mojang</id>
<url>https://libraries.minecraft.net</url>
</repository>
</repositories>
<dependencies>
@ -141,6 +147,14 @@
<version>5.3.1</version>
<scope>test</scope>
</dependency>
<!-- Snake YAML -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.18</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View File

@ -42,7 +42,7 @@ public class SpongeEncodeHandler extends MessageToByteEncoder<Object> implements
@Override
public void transform(ByteBuf bytebuf) throws Exception {
info.checkOutgoingPacket();
if (!info.checkOutgoingPacket()) throw CancelEncoderException.generate(null);
if (!info.shouldTransformPacket()) return;
info.transformOutgoing(bytebuf, CancelEncoderException::generate);
}

View File

@ -42,7 +42,7 @@
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>1.1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@ -26,11 +26,7 @@ import us.myles.ViaVersion.util.GsonUtil;
import us.myles.ViaVersion.velocity.VersionInfo;
import us.myles.ViaVersion.velocity.command.VelocityCommandHandler;
import us.myles.ViaVersion.velocity.command.VelocityCommandSender;
import us.myles.ViaVersion.velocity.platform.VelocityTaskId;
import us.myles.ViaVersion.velocity.platform.VelocityViaAPI;
import us.myles.ViaVersion.velocity.platform.VelocityViaConfig;
import us.myles.ViaVersion.velocity.platform.VelocityViaInjector;
import us.myles.ViaVersion.velocity.platform.VelocityViaLoader;
import us.myles.ViaVersion.velocity.platform.*;
import us.myles.ViaVersion.velocity.service.ProtocolDetectorService;
import us.myles.ViaVersion.velocity.util.LoggerWrapper;

View File

@ -7,12 +7,14 @@ import us.myles.ViaVersion.api.protocol.ProtocolPipeline;
import java.lang.reflect.Method;
public class VelocityChannelInitializer extends ChannelInitializer {
private final ChannelInitializer original;
public class VelocityChannelInitializer extends ChannelInitializer<Channel> {
private final ChannelInitializer<?> original;
private final boolean clientSide;
private static Method initChannel;
public VelocityChannelInitializer(ChannelInitializer original) {
public VelocityChannelInitializer(ChannelInitializer<?> original, boolean clientSide) {
this.original = original;
this.clientSide = clientSide;
}
static {
@ -28,7 +30,7 @@ public class VelocityChannelInitializer extends ChannelInitializer {
protected void initChannel(Channel channel) throws Exception {
initChannel.invoke(original, channel);
UserConnection user = new UserConnection(channel);
UserConnection user = new UserConnection(channel, clientSide);
new ProtocolPipeline(user);
// We need to add a separated handler because Velocity uses pipeline().get(MINECRAFT_DECODER)

View File

@ -3,16 +3,21 @@ package us.myles.ViaVersion.velocity.handlers;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.MessageToMessageDecoder;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.exception.CancelDecoderException;
import us.myles.ViaVersion.exception.CancelCodecException;
import us.myles.ViaVersion.exception.CancelDecoderException;
import us.myles.ViaVersion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
@ChannelHandler.Sharable
public class VelocityDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
private final UserConnection info;
private boolean handledCompression;
private boolean skipDoubleTransform;
public VelocityDecodeHandler(UserConnection info) {
this.info = info;
@ -20,6 +25,12 @@ public class VelocityDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
if (skipDoubleTransform) {
skipDoubleTransform = false;
out.add(bytebuf.retain());
return;
}
if (!info.checkIncomingPacket()) throw CancelDecoderException.generate(null);
if (!info.shouldTransformPacket()) {
out.add(bytebuf.retain());
@ -28,13 +39,55 @@ public class VelocityDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
try {
boolean needsCompress = handleCompressionOrder(ctx, transformedBuf);
info.transformIncoming(transformedBuf, CancelDecoderException::generate);
if (needsCompress) {
recompress(ctx, transformedBuf);
skipDoubleTransform = true;
}
out.add(transformedBuf.retain());
} finally {
transformedBuf.release();
}
}
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {
if (handledCompression) return false;
int decoderIndex = ctx.pipeline().names().indexOf("compression-decoder");
if (decoderIndex == -1) return false;
handledCompression = true;
if (decoderIndex > ctx.pipeline().names().indexOf("via-decoder")) {
// Need to decompress this packet due to bad order
ByteBuf decompressed = (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder<?>) ctx.pipeline().get("compression-decoder"), ctx, buf).get(0);
try {
buf.clear().writeBytes(decompressed);
} finally {
decompressed.release();
}
ChannelHandler encoder = ctx.pipeline().get("via-encoder");
ChannelHandler decoder = ctx.pipeline().get("via-decoder");
ctx.pipeline().remove(encoder);
ctx.pipeline().remove(decoder);
ctx.pipeline().addAfter("compression-encoder", "via-encoder", encoder);
ctx.pipeline().addAfter("compression-decoder", "via-decoder", decoder);
return true;
}
return false;
}
private void recompress(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
ByteBuf compressed = ctx.alloc().buffer();
try {
PipelineUtil.callEncode((MessageToByteEncoder<?>) ctx.pipeline().get("compression-encoder"), ctx, buf, compressed);
buf.clear().writeBytes(compressed);
} finally {
compressed.release();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (cause instanceof CancelCodecException) return;

View File

@ -25,7 +25,7 @@ public class VelocityEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
@Override
protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
info.checkOutgoingPacket();
if (!info.checkOutgoingPacket()) throw CancelEncoderException.generate(null);
if (!info.shouldTransformPacket()) {
out.add(bytebuf.retain());
return;
@ -47,9 +47,12 @@ public class VelocityEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
}
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {
boolean needsCompress = false;
if (!handledCompression
&& ctx.pipeline().names().indexOf("compression-encoder") > ctx.pipeline().names().indexOf("via-encoder")) {
if (handledCompression) return false;
int encoderIndex = ctx.pipeline().names().indexOf("compression-encoder");
if (encoderIndex == -1) return false;
handledCompression = true;
if (encoderIndex > ctx.pipeline().names().indexOf("via-encoder")) {
// Need to decompress this packet due to bad order
ByteBuf decompressed = (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder<?>) ctx.pipeline().get("compression-decoder"), ctx, buf).get(0);
try {
@ -63,10 +66,9 @@ public class VelocityEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
ctx.pipeline().remove(decoder);
ctx.pipeline().addAfter("compression-encoder", "via-encoder", encoder);
ctx.pipeline().addAfter("compression-decoder", "via-decoder", decoder);
needsCompress = true;
handledCompression = true;
return true;
}
return needsCompress;
return false;
}
private void recompress(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {

View File

@ -1,182 +0,0 @@
package us.myles.ViaVersion.velocity.handlers;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.protocol.ProtocolPipeline;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.packets.InventoryPackets;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import us.myles.ViaVersion.velocity.service.ProtocolDetectorService;
import us.myles.ViaVersion.velocity.storage.VelocityStorage;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class VelocityServerHandler {
private static Method setProtocolVersion;
private static Method setNextProtocolVersion;
private static Method getMinecraftConnection;
private static Method getNextProtocolVersion;
private static Method getKnownChannels;
static {
try {
setProtocolVersion = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection")
.getDeclaredMethod("setProtocolVersion", ProtocolVersion.class);
setNextProtocolVersion = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection")
.getDeclaredMethod("setNextProtocolVersion", ProtocolVersion.class);
Class<?> connectedPlayer = Class.forName("com.velocitypowered.proxy.connection.client.ConnectedPlayer");
getMinecraftConnection = connectedPlayer.getDeclaredMethod("getMinecraftConnection");
getNextProtocolVersion = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection")
.getDeclaredMethod("getNextProtocolVersion");
getKnownChannels = connectedPlayer.getDeclaredMethod("getKnownChannels");
} catch (NoSuchMethodException | ClassNotFoundException e) {
e.printStackTrace();
}
}
@Subscribe
public void preServerConnect(ServerPreConnectEvent e) {
try {
UserConnection user = Via.getManager().getConnection(e.getPlayer().getUniqueId());
if (user == null) return;
if (!user.has(VelocityStorage.class)) {
user.put(new VelocityStorage(user, e.getPlayer()));
}
int protocolId = ProtocolDetectorService.getProtocolId(e.getOriginalServer().getServerInfo().getName());
List<Pair<Integer, Protocol>> protocols = ProtocolRegistry.getProtocolPath(user.getProtocolInfo().getProtocolVersion(), protocolId);
// Check if ViaVersion can support that version
Object connection = getMinecraftConnection.invoke(e.getPlayer());
setNextProtocolVersion.invoke(connection, ProtocolVersion.getProtocolVersion(protocols == null
? user.getProtocolInfo().getProtocolVersion()
: protocolId));
} catch (IllegalAccessException | InvocationTargetException e1) {
e1.printStackTrace();
}
}
@Subscribe(order = PostOrder.LATE)
public void connectedEvent(ServerConnectedEvent e) {
UserConnection user = Via.getManager().getConnection(e.getPlayer().getUniqueId());
CompletableFuture.runAsync(() -> {
try {
checkServerChange(e, Via.getManager().getConnection(e.getPlayer().getUniqueId()));
} catch (Exception e1) {
e1.printStackTrace();
}
}, user.getChannel().eventLoop()).join();
}
public void checkServerChange(ServerConnectedEvent e, UserConnection user) throws Exception {
if (user == null) return;
// Handle server/version change
if (user.has(VelocityStorage.class)) {
VelocityStorage storage = user.get(VelocityStorage.class);
if (e.getServer() != null) {
if (!e.getServer().getServerInfo().getName().equals(storage.getCurrentServer())) {
String serverName = e.getServer().getServerInfo().getName();
storage.setCurrentServer(serverName);
int protocolId = ProtocolDetectorService.getProtocolId(serverName);
if (protocolId <= ProtocolVersion.MINECRAFT_1_8.getProtocol()) { // 1.8 doesn't have BossBar packet
if (storage.getBossbar() != null) {
// TODO: Verify whether this packet needs to be sent when 1.8 -> 1.9 protocol isn't present in the pipeline
// This ensures we can encode it properly as only the 1.9 protocol is currently implemented.
if (user.getProtocolInfo().getPipeline().contains(Protocol1_9To1_8.class)) {
for (UUID uuid : storage.getBossbar()) {
PacketWrapper wrapper = new PacketWrapper(0x0C, null, user);
wrapper.write(Type.UUID, uuid);
wrapper.write(Type.VAR_INT, 1); // remove
wrapper.send(Protocol1_9To1_8.class, true, true);
}
}
storage.getBossbar().clear();
}
}
ProtocolInfo info = user.getProtocolInfo();
int previousServerProtocol = info.getServerProtocolVersion();
// Refresh the pipes
List<Pair<Integer, Protocol>> protocols = ProtocolRegistry.getProtocolPath(info.getProtocolVersion(), protocolId);
ProtocolPipeline pipeline = info.getPipeline();
user.clearStoredObjects();
pipeline.cleanPipes();
if (protocols == null) {
// TODO Check Bungee Supported Protocols? *shrugs*
protocolId = info.getProtocolVersion();
} else {
for (Pair<Integer, Protocol> prot : protocols) {
pipeline.add(prot.getValue());
}
}
info.setServerProtocolVersion(protocolId);
// Add version-specific base Protocol
pipeline.add(ProtocolRegistry.getBaseProtocol(protocolId));
Collection<String> knownChannels = (Collection<String>) getKnownChannels.invoke(e.getPlayer());
if (previousServerProtocol != -1) {
int id1_13 = ProtocolVersion.MINECRAFT_1_13.getProtocol();
if (previousServerProtocol < id1_13 && protocolId >= id1_13) {
List<String> newChannels = new ArrayList<>();
for (String oldChannel : knownChannels) {
String transformed = InventoryPackets.getNewPluginChannelId(oldChannel);
if (transformed != null) {
newChannels.add(transformed);
}
}
knownChannels.clear();
knownChannels.addAll(newChannels);
} else if (previousServerProtocol >= id1_13 && protocolId < id1_13) {
List<String> newChannels = new ArrayList<>();
for (String oldChannel : knownChannels) {
String transformed = InventoryPackets.getOldPluginChannelId(oldChannel);
if (transformed != null) {
newChannels.add(transformed);
}
}
knownChannels.clear();
knownChannels.addAll(newChannels);
}
}
user.put(info);
user.put(storage);
user.setActive(protocols != null);
// Init all protocols TODO check if this can get moved up to the previous for loop, and doesn't require the pipeline to already exist.
for (Protocol protocol : pipeline.pipes()) {
protocol.init(user);
}
Object connection = getMinecraftConnection.invoke(e.getPlayer());
ProtocolVersion version = (ProtocolVersion) getNextProtocolVersion.invoke(connection);
setProtocolVersion.invoke(connection, version);
}
}
}
}
}

View File

@ -1,44 +0,0 @@
package us.myles.ViaVersion.velocity.listeners;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.minecraft.metadata.types.MetaType1_9;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.version.Types1_9;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.EntityTracker1_9;
import java.util.Collections;
/*
* This patches https://github.com/ViaVersion/ViaVersion/issues/555
*/
public class ElytraPatch {
@Subscribe(order = PostOrder.LAST)
public void onServerConnected(ServerConnectedEvent event) {
UserConnection user = Via.getManager().getConnection(event.getPlayer().getUniqueId());
if (user == null) return;
try {
if (user.getProtocolInfo().getPipeline().contains(Protocol1_9To1_8.class)) {
int entityId = user.get(EntityTracker1_9.class).getProvidedEntityId();
PacketWrapper wrapper = new PacketWrapper(0x39, null, user);
wrapper.write(Type.VAR_INT, entityId);
wrapper.write(Types1_9.METADATA_LIST, Collections.singletonList(new Metadata(0, MetaType1_9.Byte, (byte) 0)));
wrapper.send(Protocol1_9To1_8.class);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -30,13 +30,25 @@ public class VelocityViaInjector implements ViaInjector {
return (ChannelInitializer) ReflectionUtil.invoke(channelInitializerHolder, "get");
}
private ChannelInitializer getBackendInitializer() throws Exception {
Object connectionManager = ReflectionUtil.get(VelocityPlugin.PROXY, "cm", Object.class);
Object channelInitializerHolder = ReflectionUtil.invoke(connectionManager, "getBackendChannelInitializer");
return (ChannelInitializer) ReflectionUtil.invoke(channelInitializerHolder, "get");
}
@Override
public void inject() throws Exception {
Object connectionManager = ReflectionUtil.get(VelocityPlugin.PROXY, "cm", Object.class);
Object channelInitializerHolder = ReflectionUtil.invoke(connectionManager, "getServerChannelInitializer");
ChannelInitializer originalInitializer = getInitializer();
channelInitializerHolder.getClass().getMethod("set", ChannelInitializer.class)
.invoke(channelInitializerHolder, new VelocityChannelInitializer(originalInitializer));
.invoke(channelInitializerHolder, new VelocityChannelInitializer(originalInitializer, false));
Object backendInitializerHolder = ReflectionUtil.invoke(connectionManager, "getBackendChannelInitializer");
ChannelInitializer backendInitializer = getBackendInitializer();
backendInitializerHolder.getClass().getMethod("set", ChannelInitializer.class)
.invoke(backendInitializerHolder, new VelocityChannelInitializer(backendInitializer, true));
}
@Override

View File

@ -9,8 +9,6 @@ import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.protocols.base.VersionProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BossBarProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider;
import us.myles.ViaVersion.velocity.handlers.VelocityServerHandler;
import us.myles.ViaVersion.velocity.listeners.ElytraPatch;
import us.myles.ViaVersion.velocity.listeners.UpdateListener;
import us.myles.ViaVersion.velocity.providers.VelocityBossBarProvider;
import us.myles.ViaVersion.velocity.providers.VelocityMovementTransmitter;
@ -26,7 +24,6 @@ public class VelocityViaLoader implements ViaPlatformLoader {
if (ProtocolRegistry.SERVER_PROTOCOL < ProtocolVersion.v1_9.getVersion()) {
Via.getManager().getProviders().use(MovementTransmitterProvider.class, new VelocityMovementTransmitter());
Via.getManager().getProviders().use(BossBarProvider.class, new VelocityBossBarProvider());
VelocityPlugin.PROXY.getEventManager().register(plugin, new ElytraPatch());
}
Via.getManager().getProviders().use(VersionProvider.class, new VelocityVersionProvider());
@ -34,7 +31,6 @@ public class VelocityViaLoader implements ViaPlatformLoader {
// We don't need main hand patch because Join Game packet makes client send hand data again
VelocityPlugin.PROXY.getEventManager().register(plugin, new UpdateListener());
VelocityPlugin.PROXY.getEventManager().register(plugin, new VelocityServerHandler());
int pingInterval = ((VelocityViaConfig) Via.getPlatform().getConf()).getVelocityPingInterval();
if (pingInterval > 0) {

View File

@ -1,19 +1,42 @@
package us.myles.ViaVersion.velocity.providers;
import com.velocitypowered.api.proxy.ServerConnection;
import io.netty.channel.ChannelHandler;
import us.myles.ViaVersion.VelocityPlugin;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.protocols.base.VersionProvider;
import us.myles.ViaVersion.velocity.platform.VelocityViaInjector;
import us.myles.ViaVersion.velocity.service.ProtocolDetectorService;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.IntStream;
public class VelocityVersionProvider extends VersionProvider {
private static Method getAssociation;
static {
try {
getAssociation = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection").getMethod("getAssociation");
} catch (NoSuchMethodException | ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override
public int getServerProtocol(UserConnection user) throws Exception {
return user.isClientSide() ? getBackProtocol(user) : getFrontProtocol(user);
}
private int getBackProtocol(UserConnection user) throws Exception {
ChannelHandler mcHandler = user.getChannel().pipeline().get("handler");
return ProtocolDetectorService.getProtocolId(
((ServerConnection) getAssociation.invoke(mcHandler)).getServerInfo().getName());
}
private int getFrontProtocol(UserConnection user) throws Exception {
int playerVersion = user.getProtocolInfo().getProtocolVersion();
IntStream versions = com.velocitypowered.api.network.ProtocolVersion.SUPPORTED_VERSIONS.stream()