diff --git a/README.md b/README.md index 0ee8f8a..509ff14 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ For a full user guide go to the [Usage for Players](#usage-for-players-gui) sect ## Supported Client versions - Release (1.7.2 - 1.19.4) +- Classic, Alpha, Beta, Release 1.0 - 1.6.4 (Only passthrough) +- Eaglercraft 1.8 ViaProxy supports joining to any of the listed server version from any of the listed client versions. diff --git a/src/main/java/net/raphimc/viaproxy/cli/options/Options.java b/src/main/java/net/raphimc/viaproxy/cli/options/Options.java index 3881e86..44bf467 100644 --- a/src/main/java/net/raphimc/viaproxy/cli/options/Options.java +++ b/src/main/java/net/raphimc/viaproxy/cli/options/Options.java @@ -59,6 +59,7 @@ public class Options { public static String RESOURCE_PACK_URL; // Example: http://example.com/resourcepack.zip public static boolean SERVER_HAPROXY_PROTOCOL; public static boolean LEGACY_CLIENT_PASSTHROUGH; + public static boolean ALLOW_EAGLERCRAFT_CLIENTS; public static void parse(final String[] args) throws IOException { final OptionParser parser = new OptionParser(); @@ -79,7 +80,8 @@ public class Options { final OptionSpec resourcePackUrl = parser.acceptsAll(asList("resource_pack_url", "resource_pack", "rpu", "rp"), "URL of a resource pack which clients can optionally download").withRequiredArg().ofType(String.class); final OptionSpec proxyUrl = parser.acceptsAll(asList("proxy_url", "proxy"), "URL of a SOCKS(4/5)/HTTP(S) proxy which will be used for backend TCP connections").withRequiredArg().ofType(String.class); final OptionSpec serverHaProxyProtocol = parser.acceptsAll(asList("server-haproxy-protocol", "server-haproxy"), "Send HAProxy protocol messages to the backend server"); - final OptionSpec legacyClientPassthrough = parser.acceptsAll(asList("legacy_client_passthrough", "legacy_passthrough"), "Allow <= 1.6.4 clients to connect to the backend server"); + final OptionSpec legacyClientPassthrough = parser.acceptsAll(asList("legacy_client_passthrough", "legacy_passthrough"), "Allow <= 1.6.4 clients to connect to the backend server (No protocol translation)"); + final OptionSpec allowEaglerCraftClients = parser.acceptsAll(asList("allow_eaglercraft_clients", "allow_eaglercraft"), "Allow EaglerCraft clients to connect to ViaProxy"); PluginManager.EVENT_MANAGER.call(new PreOptionsParseEvent(parser)); final OptionSet options = parser.parse(args); @@ -119,6 +121,7 @@ public class Options { } SERVER_HAPROXY_PROTOCOL = options.has(serverHaProxyProtocol); LEGACY_CLIENT_PASSTHROUGH = options.has(legacyClientPassthrough); + ALLOW_EAGLERCRAFT_CLIENTS = options.has(allowEaglerCraftClients); PluginManager.EVENT_MANAGER.call(new PostOptionsParseEvent(options)); } diff --git a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyChannelInitializer.java b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyChannelInitializer.java index 8a4bae9..e6d455d 100644 --- a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyChannelInitializer.java +++ b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyChannelInitializer.java @@ -26,11 +26,24 @@ import net.raphimc.viaproxy.cli.options.Options; import net.raphimc.viaproxy.plugins.PluginManager; import net.raphimc.viaproxy.plugins.events.Client2ProxyChannelInitializeEvent; import net.raphimc.viaproxy.plugins.events.types.ITyped; +import net.raphimc.viaproxy.proxy.client2proxy.eaglercraft.EaglercraftInitialHandler; +import net.raphimc.viaproxy.proxy.client2proxy.passthrough.LegacyClientPassthroughHandler; import java.util.function.Supplier; public class Client2ProxyChannelInitializer extends MinecraftChannelInitializer { + public static final String EAGLERCRAFT_INITIAL_HANDLER_NAME = "eaglercraft-initial-handler"; + public static final String WEBSOCKET_SSL_HANDLER_NAME = "ws-ssl-handler"; + public static final String WEBSOCKET_HTTP_CODEC_NAME = "ws-http-codec"; + public static final String WEBSOCKET_HTTP_AGGREGATOR_NAME = "ws-http-aggregator"; + public static final String WEBSOCKET_COMPRESSION_NAME = "ws-compression"; + public static final String WEBSOCKET_HANDLER_NAME = "ws-handler"; + public static final String WEBSOCKET_ACTIVE_NOTIFIER_NAME = "ws-active-notifier"; + public static final String EAGLERCRAFT_HANDLER_NAME = "eaglercraft-handler"; + + public static final String LEGACY_PASSTHROUGH_HANDLER_NAME = "legacy-passthrough-handler"; + public Client2ProxyChannelInitializer(final Supplier handlerSupplier) { super(handlerSupplier); } @@ -42,9 +55,13 @@ public class Client2ProxyChannelInitializer extends MinecraftChannelInitializer return; } - if (Options.LEGACY_CLIENT_PASSTHROUGH) { - channel.pipeline().addLast("legacy-passthrough-handler", new LegacyClientPassthroughHandler()); + if (Options.ALLOW_EAGLERCRAFT_CLIENTS) { + channel.pipeline().addLast(EAGLERCRAFT_INITIAL_HANDLER_NAME, new EaglercraftInitialHandler()); } + if (Options.LEGACY_CLIENT_PASSTHROUGH) { + channel.pipeline().addLast(LEGACY_PASSTHROUGH_HANDLER_NAME, new LegacyClientPassthroughHandler()); + } + super.initChannel(channel); channel.attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getHandshakeRegistry(false)); diff --git a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/EaglercraftHandler.java b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/EaglercraftHandler.java new file mode 100644 index 0000000..10f839a --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/EaglercraftHandler.java @@ -0,0 +1,266 @@ +/* + * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.raphimc.viaproxy.proxy.client2proxy.eaglercraft; + +import com.google.common.net.HostAndPort; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageCodec; +import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; +import net.raphimc.netminecraft.constants.ConnectionState; +import net.raphimc.netminecraft.constants.MCPackets; +import net.raphimc.netminecraft.constants.MCPipeline; +import net.raphimc.netminecraft.packet.PacketTypes; +import net.raphimc.viaproxy.ViaProxy; +import net.raphimc.viaproxy.util.logging.Logger; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +// Thanks to ayunami2000 for helping with the eaglercraft protocol +public class EaglercraftHandler extends MessageToMessageCodec { + + private static final int CLIENT_VERSION = 0x01; + private static final int SERVER_VERSION = 0x02; + private static final int VERSION_MISMATCH = 0x03; + private static final int CLIENT_REQUEST_LOGIN = 0x04; + private static final int SERVER_ALLOW_LOGIN = 0x05; + private static final int SERVER_DENY_LOGIN = 0x06; + private static final int CLIENT_PROFILE_DATA = 0x07; + private static final int CLIENT_FINISH_LOGIN = 0x08; + private static final int SERVER_FINISH_LOGIN = 0x09; + private static final int SERVER_ERROR = 0xFF; + + private HostAndPort host; + private State state = State.PRE_HANDSHAKE; + private int protocolVersion; + private String username; + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ctx.attr(MCPipeline.COMPRESSION_THRESHOLD_ATTRIBUTE_KEY).set(-2); // Disable automatic compression in Proxy2ServerHandler + ctx.pipeline().remove(MCPipeline.SIZER_HANDLER_NAME); + super.channelActive(ctx); + } + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf in, List out) { + if (this.state != State.LOGIN_COMPLETE) { + throw new IllegalStateException("Cannot send packets before login is completed"); + } + + out.add(new BinaryWebSocketFrame(in.retain())); + } + + @Override + protected void decode(ChannelHandlerContext ctx, WebSocketFrame in, List out) { + if (in instanceof BinaryWebSocketFrame) { + final ByteBuf data = in.content(); + + switch (this.state) { + case PRE_HANDSHAKE: + if (data.readableBytes() >= 2) { // Check for legacy client + if (data.getByte(0) == (byte) 2 && data.getByte(1) == (byte) 69) { + throw new IllegalStateException("Your client is not yet supported"); + } + } + this.state = State.HANDSHAKE; + case HANDSHAKE: + int packetId = data.readUnsignedByte(); // packet id + if (packetId == CLIENT_VERSION) { + int eaglercraftVersion = data.readUnsignedByte(); // eaglercraft version + final int minecraftVersion; + if (eaglercraftVersion == 1) { + minecraftVersion = data.readUnsignedByte(); // minecraft version + } else if (eaglercraftVersion == 2) { + int count = data.readUnsignedShort(); // eaglercraft versions + final List eaglercraftVersions = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + eaglercraftVersions.add(data.readUnsignedShort()); // eaglercraft version id + } + if (!eaglercraftVersions.contains(2) && !eaglercraftVersions.contains(3)) { + Logger.LOGGER.error("No supported eaglercraft versions found"); + ctx.close(); + return; + } + + if (eaglercraftVersions.contains(3)) { + eaglercraftVersion = 3; + } + + count = data.readUnsignedShort(); // minecraft versions + final List minecraftVersions = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + minecraftVersions.add(data.readUnsignedShort()); // minecraft version id + } + if (minecraftVersions.size() != 1) { + Logger.LOGGER.error("No supported minecraft versions found"); + ctx.close(); + } + minecraftVersion = minecraftVersions.get(0); + } else { + throw new IllegalArgumentException("Unknown Eaglercraft version: " + eaglercraftVersion); + } + final String clientBrand = data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); // client brand + final String clientVersionString = data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); // client version + + if (eaglercraftVersion >= 2) { + data.skipBytes(1); // auth enabled + data.skipBytes(data.readUnsignedByte()); // auth username + } + if (data.isReadable()) { + throw new IllegalArgumentException("Too much data in packet: " + data.readableBytes() + " bytes"); + } + + this.state = State.HANDSHAKE_COMPLETE; + this.protocolVersion = minecraftVersion; + Logger.LOGGER.info("Eaglercraft client connected: " + clientBrand + " " + clientVersionString); + final String serverBrand = "ViaProxy"; + final String serverVersionString = ViaProxy.VERSION; + + final ByteBuf response = ctx.alloc().buffer(); + response.writeByte(SERVER_VERSION); // packet id + if (eaglercraftVersion == 1) { + response.writeByte(1); // eaglercraft version + } else { + response.writeShort(eaglercraftVersion); // eaglercraft version + response.writeShort(minecraftVersion); // minecraft version + } + response.writeByte(serverBrand.length()).writeCharSequence(serverBrand, StandardCharsets.US_ASCII); // server brand + response.writeByte(serverVersionString.length()).writeCharSequence(serverVersionString, StandardCharsets.US_ASCII); // server version + response.writeByte(0); // auth method + response.writeShort(0); // salt length + ctx.writeAndFlush(new BinaryWebSocketFrame(response)); + } else { + throw new IllegalStateException("Unexpected packet id " + packetId + " in state " + this.state); + } + break; + case HANDSHAKE_COMPLETE: + packetId = data.readUnsignedByte(); // packet id + if (packetId == CLIENT_REQUEST_LOGIN) { + final String username = data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); // username + data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); // requested server + data.skipBytes(data.readUnsignedByte()); // auth password + if (data.isReadable()) { + throw new IllegalArgumentException("Too much data in packet: " + data.readableBytes() + " bytes"); + } + + this.state = State.LOGIN; + this.username = username; + final UUID uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8)); + + final ByteBuf response = ctx.alloc().buffer(); + response.writeByte(SERVER_ALLOW_LOGIN); // packet id + response.writeByte(username.length()).writeCharSequence(username, StandardCharsets.US_ASCII); // username + response.writeLong(uuid.getMostSignificantBits()).writeLong(uuid.getLeastSignificantBits()); // uuid + ctx.writeAndFlush(new BinaryWebSocketFrame(response)); + } else { + throw new IllegalStateException("Unexpected packet id " + packetId + " in state " + this.state); + } + break; + case LOGIN: + packetId = data.readUnsignedByte(); // packet id + if (packetId == CLIENT_PROFILE_DATA) { + final String type = data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); + final byte[] dataBytes = new byte[data.readUnsignedShort()]; + data.readBytes(dataBytes); + if (data.isReadable()) { + throw new IllegalArgumentException("Too much data in packet: " + data.readableBytes() + " bytes"); + } + } else if (packetId == CLIENT_FINISH_LOGIN) { + if (data.isReadable()) { + throw new IllegalArgumentException("Too much data in packet: " + data.readableBytes() + " bytes"); + } + this.state = State.LOGIN_COMPLETE; + + final int handshakeId = MCPackets.C2S_HANDSHAKE.getId(this.protocolVersion); + final int loginHelloId = MCPackets.C2S_LOGIN_HELLO.getId(this.protocolVersion); + + if (handshakeId == -1 || loginHelloId == -1) { + Logger.LOGGER.error("Unsupported protocol version: " + this.protocolVersion); + ctx.close(); + return; + } + + final ByteBuf handshake = ctx.alloc().buffer(); + PacketTypes.writeVarInt(handshake, handshakeId); // packet id + PacketTypes.writeVarInt(handshake, this.protocolVersion); // protocol version + PacketTypes.writeString(handshake, this.host.getHost()); // address + handshake.writeShort(this.host.getPort()); // port + PacketTypes.writeVarInt(handshake, ConnectionState.LOGIN.getId()); // next state + out.add(handshake); + + final ByteBuf loginHello = ctx.alloc().buffer(); + PacketTypes.writeVarInt(loginHello, loginHelloId); // packet id + PacketTypes.writeString(loginHello, this.username); // username + out.add(loginHello); + + final ByteBuf response = ctx.alloc().buffer(); + response.writeByte(SERVER_FINISH_LOGIN); // packet id + ctx.writeAndFlush(new BinaryWebSocketFrame(response)); + } else { + throw new IllegalStateException("Unexpected packet id " + packetId + " in state " + this.state); + } + break; + case LOGIN_COMPLETE: + out.add(data.retain()); + break; + default: + throw new IllegalStateException("Unexpected binary frame in state " + this.state); + } + } else if (in instanceof TextWebSocketFrame) { + final String text = ((TextWebSocketFrame) in).text(); + ctx.close(); + + if (this.state != State.PRE_HANDSHAKE) { + throw new IllegalStateException("Unexpected text frame in state " + this.state); + } + } else { + throw new UnsupportedOperationException("Unsupported frame type: " + in.getClass().getName()); + } + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) { + final WebSocketServerProtocolHandler.HandshakeComplete handshake = (WebSocketServerProtocolHandler.HandshakeComplete) evt; + if (!handshake.requestHeaders().contains("Host")) { + ctx.close(); + return; + } + + this.host = HostAndPort.fromString(handshake.requestHeaders().get("Host")).withDefaultPort(80); + } + + super.userEventTriggered(ctx, evt); + } + + public enum State { + PRE_HANDSHAKE, + HANDSHAKE, + HANDSHAKE_COMPLETE, + LOGIN, + LOGIN_COMPLETE, + } + +} diff --git a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/EaglercraftInitialHandler.java b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/EaglercraftInitialHandler.java new file mode 100644 index 0000000..fbd2f4f --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/EaglercraftInitialHandler.java @@ -0,0 +1,77 @@ +/* + * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.raphimc.viaproxy.proxy.client2proxy.eaglercraft; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; +import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyChannelInitializer; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.List; + +public class EaglercraftInitialHandler extends ByteToMessageDecoder { + + private static SslContext sslContext; + + static { + final File certFolder = new File("certs"); + if (certFolder.exists()) { + try { + sslContext = SslContextBuilder.forServer(new File(certFolder, "fullchain.pem"), new File(certFolder, "privkey.pem")).build(); + } catch (Throwable e) { + throw new RuntimeException("Failed to load SSL context", e); + } + } + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { + if (!ctx.channel().isOpen()) return; + if (!in.isReadable()) return; + + if (in.readableBytes() >= 3) { + final byte[] data = new byte[3]; + in.getBytes(0, data); + final String method = new String(data, StandardCharsets.UTF_8); + if (method.equals("GET")) { // Websocket request + if (sslContext != null) { + ctx.pipeline().addBefore(Client2ProxyChannelInitializer.EAGLERCRAFT_INITIAL_HANDLER_NAME, Client2ProxyChannelInitializer.WEBSOCKET_SSL_HANDLER_NAME, sslContext.newHandler(ctx.alloc())); + } + + ctx.pipeline().addBefore(Client2ProxyChannelInitializer.EAGLERCRAFT_INITIAL_HANDLER_NAME, Client2ProxyChannelInitializer.WEBSOCKET_HTTP_CODEC_NAME, new HttpServerCodec()); + ctx.pipeline().addBefore(Client2ProxyChannelInitializer.EAGLERCRAFT_INITIAL_HANDLER_NAME, Client2ProxyChannelInitializer.WEBSOCKET_HTTP_AGGREGATOR_NAME, new HttpObjectAggregator(65535, true)); + ctx.pipeline().addBefore(Client2ProxyChannelInitializer.EAGLERCRAFT_INITIAL_HANDLER_NAME, Client2ProxyChannelInitializer.WEBSOCKET_COMPRESSION_NAME, new WebSocketServerCompressionHandler()); + ctx.pipeline().addBefore(Client2ProxyChannelInitializer.EAGLERCRAFT_INITIAL_HANDLER_NAME, Client2ProxyChannelInitializer.WEBSOCKET_HANDLER_NAME, new WebSocketServerProtocolHandler("/", null, true)); + ctx.pipeline().addBefore(Client2ProxyChannelInitializer.EAGLERCRAFT_INITIAL_HANDLER_NAME, Client2ProxyChannelInitializer.WEBSOCKET_ACTIVE_NOTIFIER_NAME, new WebSocketActiveNotifier()); + ctx.pipeline().addBefore(Client2ProxyChannelInitializer.EAGLERCRAFT_INITIAL_HANDLER_NAME, Client2ProxyChannelInitializer.EAGLERCRAFT_HANDLER_NAME, new EaglercraftHandler()); + } + + ctx.pipeline().remove(this); + ctx.pipeline().fireChannelRead(in.retain()); + } + } + +} diff --git a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/WebSocketActiveNotifier.java b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/WebSocketActiveNotifier.java new file mode 100644 index 0000000..3906e48 --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/WebSocketActiveNotifier.java @@ -0,0 +1,40 @@ +/* + * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.raphimc.viaproxy.proxy.client2proxy.eaglercraft; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; + +public class WebSocketActiveNotifier extends ChannelInboundHandlerAdapter { + + @Override + public void channelActive(ChannelHandlerContext ctx) { + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) { + ctx.fireChannelActive(); + ctx.pipeline().remove(this); + } + + super.userEventTriggered(ctx, evt); + } + +} diff --git a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/LegacyClientPassthroughHandler.java b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/passthrough/LegacyClientPassthroughHandler.java similarity index 98% rename from src/main/java/net/raphimc/viaproxy/proxy/client2proxy/LegacyClientPassthroughHandler.java rename to src/main/java/net/raphimc/viaproxy/proxy/client2proxy/passthrough/LegacyClientPassthroughHandler.java index c861a38..b88b4de 100644 --- a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/LegacyClientPassthroughHandler.java +++ b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/passthrough/LegacyClientPassthroughHandler.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package net.raphimc.viaproxy.proxy.client2proxy; +package net.raphimc.viaproxy.proxy.client2proxy.passthrough; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; diff --git a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java index f4149e1..388004b 100644 --- a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java +++ b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java @@ -45,6 +45,9 @@ import java.util.function.Supplier; public class Proxy2ServerChannelInitializer extends MinecraftChannelInitializer { + public static final String VIAPROXY_PROXY_HANDLER_NAME = "viaproxy-proxy-handler"; + public static final String VIAPROXY_HAPROXY_ENCODER_NAME = "viaproxy-haproxy-encoder"; + public Proxy2ServerChannelInitializer(final Supplier handlerSupplier) { super(handlerSupplier); } @@ -62,10 +65,10 @@ public class Proxy2ServerChannelInitializer extends MinecraftChannelInitializer proxyConnection.setUserConnection(user); if (Options.PROXY_URL != null && !proxyConnection.getServerVersion().equals(VersionEnum.bedrockLatest)) { - channel.pipeline().addLast("viaproxy-proxy-handler", this.getProxyHandler()); + channel.pipeline().addLast(VIAPROXY_PROXY_HANDLER_NAME, this.getProxyHandler()); } if (Options.SERVER_HAPROXY_PROTOCOL) { - channel.pipeline().addLast("viaproxy-haproxy-encoder", HAProxyMessageEncoder.INSTANCE); + channel.pipeline().addLast(VIAPROXY_HAPROXY_ENCODER_NAME, HAProxyMessageEncoder.INSTANCE); } super.initChannel(channel); diff --git a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java index 86cf1ab..e0bb25a 100644 --- a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java +++ b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java @@ -137,7 +137,7 @@ public class Proxy2ServerHandler extends SimpleChannelInboundHandler { private void handleLoginSuccess(final S2CLoginSuccessPacket1_7 packet) throws Exception { if (this.proxyConnection.getClientVersion().isNewerThanOrEqualTo(VersionEnum.r1_8)) { - if (Options.COMPRESSION_THRESHOLD > -1) { + if (Options.COMPRESSION_THRESHOLD > -1 && this.proxyConnection.getC2P().attr(MCPipeline.COMPRESSION_THRESHOLD_ATTRIBUTE_KEY).get() == -1) { this.proxyConnection.getC2P().writeAndFlush(new S2CLoginCompressionPacket(Options.COMPRESSION_THRESHOLD)).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE).await(); this.proxyConnection.getC2P().attr(MCPipeline.COMPRESSION_THRESHOLD_ATTRIBUTE_KEY).set(Options.COMPRESSION_THRESHOLD); }