mirror of
https://github.com/ViaVersion/ViaProxy.git
synced 2024-11-26 17:36:21 -05:00
Added server version auto detect support
This commit is contained in:
parent
de82e4c598
commit
1706b8c718
6 changed files with 150 additions and 20 deletions
|
@ -85,6 +85,9 @@ dependencies {
|
|||
include "net.lenni0451.classtransform:additionalclassprovider:1.12.1"
|
||||
include "net.lenni0451:Reflect:1.3.0"
|
||||
include "net.lenni0451:LambdaEvents:2.3.2"
|
||||
include("net.lenni0451:MCPing:1.3.0") {
|
||||
exclude group: "com.google.code.gson", module: "gson"
|
||||
}
|
||||
include("net.raphimc.netminecraft:all:2.3.7-SNAPSHOT") {
|
||||
exclude group: "com.google.code.gson", module: "gson"
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import net.raphimc.netminecraft.constants.MCPipeline;
|
|||
import net.raphimc.netminecraft.netty.connection.NetServer;
|
||||
import net.raphimc.viaproxy.cli.ConsoleHandler;
|
||||
import net.raphimc.viaproxy.cli.options.Options;
|
||||
import net.raphimc.viaproxy.injection.VersionEnumExtension;
|
||||
import net.raphimc.viaproxy.plugins.PluginManager;
|
||||
import net.raphimc.viaproxy.plugins.events.Client2ProxyHandlerCreationEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.ProxyStartEvent;
|
||||
|
@ -132,7 +133,8 @@ public class ViaProxy {
|
|||
|
||||
ConsoleHandler.hookConsole();
|
||||
ClassLoaderPriorityUtil.loadOverridingJars();
|
||||
loadNetty();
|
||||
ViaProxy.loadNetty();
|
||||
VersionEnumExtension.init();
|
||||
|
||||
SAVE_MANAGER = new SaveManager();
|
||||
PLUGIN_MANAGER = new PluginManager();
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.raphimc.viaproxy.injection;
|
||||
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import net.lenni0451.reflect.Enums;
|
||||
import net.raphimc.vialoader.util.VersionEnum;
|
||||
|
||||
public class VersionEnumExtension {
|
||||
|
||||
private static final ProtocolVersion autoDetect = ProtocolVersion.register(-2, "Auto Detect (1.7+ servers)");
|
||||
public static final VersionEnum AUTO_DETECT = Enums.newInstance(VersionEnum.class, "AUTO_DETECT", VersionEnum.UNKNOWN.ordinal(), new Class<?>[]{ProtocolVersion.class}, new Object[]{autoDetect});
|
||||
|
||||
static {
|
||||
Enums.addEnumInstance(VersionEnum.class, AUTO_DETECT);
|
||||
VersionEnum.SORTED_VERSIONS.add(AUTO_DETECT);
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
// calls static initializer
|
||||
}
|
||||
|
||||
}
|
|
@ -21,12 +21,16 @@ import com.google.common.collect.ImmutableSet;
|
|||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import com.viaversion.viaversion.api.protocol.version.VersionRange;
|
||||
import com.viaversion.viaversion.util.Pair;
|
||||
import net.raphimc.viaproxy.injection.VersionEnumExtension;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -35,6 +39,10 @@ import java.util.Set;
|
|||
@Mixin(value = ProtocolVersion.class, remap = false)
|
||||
public abstract class MixinProtocolVersion {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private int version;
|
||||
|
||||
@Unique
|
||||
private static Set<String> skips;
|
||||
|
||||
|
@ -89,4 +97,11 @@ public abstract class MixinProtocolVersion {
|
|||
return ProtocolVersion.register(version, name, versionRange);
|
||||
}
|
||||
|
||||
@Inject(method = "isKnown", at = @At("HEAD"), cancellable = true)
|
||||
private void markAutoDetectAsUnknown(CallbackInfoReturnable<Boolean> cir) {
|
||||
if (VersionEnumExtension.AUTO_DETECT != null && this.version == VersionEnumExtension.AUTO_DETECT.getVersion()) {
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import net.raphimc.netminecraft.util.ServerAddress;
|
|||
import net.raphimc.vialoader.util.VersionEnum;
|
||||
import net.raphimc.viaproxy.ViaProxy;
|
||||
import net.raphimc.viaproxy.cli.options.Options;
|
||||
import net.raphimc.viaproxy.injection.VersionEnumExtension;
|
||||
import net.raphimc.viaproxy.plugins.events.ConnectEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.PreConnectEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.Proxy2ServerHandlerCreationEvent;
|
||||
|
@ -44,12 +45,14 @@ import net.raphimc.viaproxy.proxy.session.ProxyConnection;
|
|||
import net.raphimc.viaproxy.proxy.session.UserOptions;
|
||||
import net.raphimc.viaproxy.proxy.util.*;
|
||||
import net.raphimc.viaproxy.util.ArrayHelper;
|
||||
import net.raphimc.viaproxy.util.ProtocolVersionDetector;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.UnresolvedAddressException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -129,13 +132,8 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
|
|||
if (arrayHelper.isIndexValid(3)) {
|
||||
classicMpPass = arrayHelper.getString(3);
|
||||
}
|
||||
for (VersionEnum v : VersionEnum.getAllVersions()) {
|
||||
if (v.getName().equalsIgnoreCase(versionString)) {
|
||||
serverVersion = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (serverVersion == null) throw CloseAndReturn.INSTANCE;
|
||||
serverVersion = VersionEnum.fromProtocolName(versionString);
|
||||
if (serverVersion == VersionEnum.UNKNOWN) throw CloseAndReturn.INSTANCE;
|
||||
} else if (Options.SRV_MODE) {
|
||||
try {
|
||||
if (handshakeParts[0].toLowerCase().contains(".viaproxy.")) {
|
||||
|
@ -150,13 +148,11 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
|
|||
connectIP = arrayHelper.getAsString(0, arrayHelper.getLength() - 3, "_");
|
||||
connectPort = arrayHelper.getInteger(arrayHelper.getLength() - 2);
|
||||
final String versionString = arrayHelper.get(arrayHelper.getLength() - 1);
|
||||
for (VersionEnum v : VersionEnum.getAllVersions()) {
|
||||
if (v.getName().replace(" ", "-").equalsIgnoreCase(versionString)) {
|
||||
serverVersion = v;
|
||||
break;
|
||||
}
|
||||
serverVersion = VersionEnum.fromProtocolName(versionString);
|
||||
if (serverVersion == VersionEnum.UNKNOWN) {
|
||||
serverVersion = VersionEnum.fromProtocolName(versionString.replace("-", " "));
|
||||
}
|
||||
if (serverVersion == null) throw CloseAndReturn.INSTANCE;
|
||||
if (serverVersion == VersionEnum.UNKNOWN) throw CloseAndReturn.INSTANCE;
|
||||
} catch (CloseAndReturn e) {
|
||||
this.proxyConnection.kickClient("§cWrong SRV syntax! §6Please use:\n§7ip_port_version.viaproxy.hostname");
|
||||
}
|
||||
|
@ -184,6 +180,27 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
|
|||
this.proxyConnection.kickClient(preConnectEvent.getCancelMessage());
|
||||
}
|
||||
|
||||
final UserOptions userOptions = new UserOptions(classicMpPass, Options.MC_ACCOUNT);
|
||||
ChannelUtil.disableAutoRead(this.proxyConnection.getC2P());
|
||||
|
||||
if (packet.intendedState == ConnectionState.LOGIN && serverVersion.equals(VersionEnumExtension.AUTO_DETECT)) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
final VersionEnum detectedVersion = ProtocolVersionDetector.get(serverAddress, clientVersion);
|
||||
this.connect(serverAddress, detectedVersion, clientVersion, packet.intendedState, userOptions, handshakeParts);
|
||||
}).exceptionally(t -> {
|
||||
if (t instanceof ConnectException || t instanceof UnresolvedAddressException) {
|
||||
this.proxyConnection.kickClient("§cCould not connect to the backend server!");
|
||||
} else {
|
||||
this.proxyConnection.kickClient("§cAutomatic protocol detection failed!\n§c" + t.getMessage());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
} else {
|
||||
this.connect(serverAddress, serverVersion, clientVersion, packet.intendedState, userOptions, handshakeParts);
|
||||
}
|
||||
}
|
||||
|
||||
private void connect(final ServerAddress serverAddress, final VersionEnum serverVersion, final VersionEnum clientVersion, final ConnectionState intendedState, final UserOptions userOptions, final String[] handshakeParts) {
|
||||
final Supplier<ChannelHandler> handlerSupplier = () -> ViaProxy.EVENT_MANAGER.call(new Proxy2ServerHandlerCreationEvent(new Proxy2ServerHandler(), false)).getHandler();
|
||||
if (serverVersion.equals(VersionEnum.bedrockLatest)) {
|
||||
this.proxyConnection = new BedrockProxyConnection(handlerSupplier, Proxy2ServerChannelInitializer::new, this.proxyConnection.getC2P());
|
||||
|
@ -192,8 +209,8 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
|
|||
}
|
||||
this.proxyConnection.getC2P().attr(ProxyConnection.PROXY_CONNECTION_ATTRIBUTE_KEY).set(this.proxyConnection);
|
||||
this.proxyConnection.setClientVersion(clientVersion);
|
||||
this.proxyConnection.setC2pConnectionState(packet.intendedState);
|
||||
this.proxyConnection.setUserOptions(new UserOptions(classicMpPass, Options.MC_ACCOUNT));
|
||||
this.proxyConnection.setC2pConnectionState(intendedState);
|
||||
this.proxyConnection.setUserOptions(userOptions);
|
||||
this.proxyConnection.getPacketHandlers().add(new StatusPacketHandler(this.proxyConnection));
|
||||
this.proxyConnection.getPacketHandlers().add(new CustomPayloadPacketHandler(this.proxyConnection));
|
||||
this.proxyConnection.getPacketHandlers().add(new CompressionPacketHandler(this.proxyConnection));
|
||||
|
@ -207,7 +224,6 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
|
|||
this.proxyConnection.getPacketHandlers().add(new ResourcePackPacketHandler(this.proxyConnection));
|
||||
this.proxyConnection.getPacketHandlers().add(new UnexpectedPacketHandler(this.proxyConnection));
|
||||
|
||||
ChannelUtil.disableAutoRead(this.proxyConnection.getC2P());
|
||||
Logger.u_info("connect", this.proxyConnection.getC2P().remoteAddress(), this.proxyConnection.getGameProfile(), "[" + clientVersion.getName() + " <-> " + serverVersion.getName() + "] Connecting to " + serverAddress.getAddress() + ":" + serverAddress.getPort());
|
||||
ViaProxy.EVENT_MANAGER.call(new ConnectEvent(this.proxyConnection));
|
||||
|
||||
|
@ -219,10 +235,10 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
|
|||
}
|
||||
|
||||
handshakeParts[0] = serverAddress.getAddress();
|
||||
final C2SHandshakePacket newHandshakePacket = new C2SHandshakePacket(clientVersion.getOriginalVersion(), String.join("\0", handshakeParts), serverAddress.getPort(), packet.intendedState);
|
||||
final C2SHandshakePacket newHandshakePacket = new C2SHandshakePacket(clientVersion.getOriginalVersion(), String.join("\0", handshakeParts), serverAddress.getPort(), intendedState);
|
||||
this.proxyConnection.getChannel().writeAndFlush(newHandshakePacket).addListeners(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE, (ChannelFutureListener) f2 -> {
|
||||
if (f2.isSuccess()) {
|
||||
this.proxyConnection.setP2sConnectionState(packet.intendedState);
|
||||
this.proxyConnection.setP2sConnectionState(intendedState);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -232,7 +248,7 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
|
|||
}, (ThrowingChannelFutureListener) f -> {
|
||||
if (!f.isSuccess()) {
|
||||
if (f.cause() instanceof ConnectException || f.cause() instanceof UnresolvedAddressException) {
|
||||
this.proxyConnection.kickClient("§cCould not connect to the backend server!\n§cTry again in a few seconds.");
|
||||
this.proxyConnection.kickClient("§cCould not connect to the backend server!");
|
||||
} else {
|
||||
Logger.LOGGER.error("Error while connecting to the backend server", f.cause());
|
||||
this.proxyConnection.kickClient("§cAn error occurred while connecting to the backend server: " + f.cause().getMessage() + "\n§cCheck the console for more information.");
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.raphimc.viaproxy.util;
|
||||
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import net.lenni0451.mcping.MCPing;
|
||||
import net.lenni0451.mcping.responses.MCPingResponse;
|
||||
import net.raphimc.netminecraft.util.ServerAddress;
|
||||
import net.raphimc.vialoader.util.VersionEnum;
|
||||
|
||||
public class ProtocolVersionDetector {
|
||||
|
||||
private static final int TIMEOUT = 1000;
|
||||
|
||||
public static VersionEnum get(final ServerAddress serverAddress, final VersionEnum clientVersion) {
|
||||
MCPingResponse response = MCPing
|
||||
.pingModern(clientVersion.getOriginalVersion())
|
||||
.address(serverAddress.getAddress(), serverAddress.getPort())
|
||||
.noResolve()
|
||||
.timeout(TIMEOUT, TIMEOUT)
|
||||
.getSync();
|
||||
|
||||
if (response.version.protocol == clientVersion.getOriginalVersion()) { // If the server is on the same version as the client, we can just connect
|
||||
return clientVersion;
|
||||
} else { // Else ping again with protocol id -1 to get the protocol id of the server
|
||||
response = MCPing
|
||||
.pingModern(-1)
|
||||
.address(serverAddress.getAddress(), serverAddress.getPort())
|
||||
.noResolve()
|
||||
.timeout(TIMEOUT, TIMEOUT)
|
||||
.getSync();
|
||||
|
||||
if (ProtocolVersion.isRegistered(response.version.protocol)) { // If the protocol is registered, we can use it
|
||||
return VersionEnum.fromProtocolId(response.version.protocol);
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported protocol version: " + response.version.protocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue