Now works with reset packet

This commit is contained in:
Adrian Bergqvist 2022-08-13 21:55:54 +02:00
parent 31e374c1e9
commit 75ea344fda
No known key found for this signature in database
GPG Key ID: FAE7D8EDE225E686
3 changed files with 125 additions and 8 deletions

View File

@ -4,7 +4,7 @@ plugins {
}
group 'org.adde0109'
version '0.4.0'
version '0.5.0'
repositories {
maven {
@ -21,6 +21,8 @@ dependencies {
implementation 'com.electronwill.night-config:toml:3.6.5'
implementation 'org.bstats:bstats-velocity:3.0.0'
implementation 'org.apache.commons:commons-collections4:4.4'
implementation 'io.netty:netty-buffer:4.1.79.Final'
implementation 'io.netty:netty-transport:4.1.79.Final'
}
shadowJar {

View File

@ -7,6 +7,8 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.api.util.ModInfo;
import java.util.*;
import io.netty.buffer.ByteBuf;
import org.slf4j.Logger;
import java.nio.charset.StandardCharsets;
@ -60,6 +62,12 @@ public class ForgeHandshakeUtils {
return stream.toByteArray();
}
public static byte[] generateResetPacket() {
ByteArrayDataOutput dataAndPacketIdStream = ByteStreams.newDataOutput();
writeVarInt(dataAndPacketIdStream,98);
return dataAndPacketIdStream.toByteArray();
}
public static class HandshakeReceiver {
private int partLength;

View File

@ -3,17 +3,38 @@ package org.adde0109.ambassador.forge;
import com.velocitypowered.api.event.Continuation;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.UuidUtils;
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.StateRegistry;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.EventLoop;
import io.netty.util.concurrent.GenericFutureListener;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.adde0109.ambassador.Ambassador;
@ -40,6 +61,11 @@ public class ForgeServerSwitchHandler {
Optional<ForgeServerConnection> forgeServerConnectionOptional = ambassador.forgeHandshakeHandler.getForgeServerConnection(event.getOriginalServer());
Optional<ForgeConnection> forgeConnection = ambassador.forgeHandshakeHandler.getForgeConnection(event.getPlayer());
if (forgeConnection.isPresent()) {
//TODO: If the client can resync without kick, we don't need to check the server.
if (true) {
continuation.resume();
return;
}
ForgeServerConnection forgeServerConnection = forgeServerConnectionOptional.orElseGet(() -> new ForgeServerConnection(event.getOriginalServer()));
forgeServerConnection.getHandshake().whenComplete((msg, ex) -> {
if (ex != null) {
@ -62,11 +88,7 @@ public class ForgeServerSwitchHandler {
if (ambassador.config.reSyncOptionForge() != AmbassadorConfig.reSyncOption.NEVER) {
if (forgeConnection.get().getTransmittedHandshake().isEmpty() || !msg.equals(forgeConnection.get().getTransmittedHandshake().get())) {
event.setResult(ServerPreConnectEvent.ServerResult.denied());
try {
reSync(event.getPlayer(),forgeServerConnection);
} catch (ReflectiveOperationException e) {
continuation.resumeWithException(e);
}
kickReSync(event.getPlayer(), forgeServerConnection);
}
}
}
@ -82,9 +104,94 @@ public class ForgeServerSwitchHandler {
continuation.resume();
}
}
private void reSync(Player player, ForgeServerConnection forgeServerConnection) throws ReflectiveOperationException {
private void kickReSync(Player player, ForgeServerConnection forgeServerConnection){
ambassador.logger.info("Kicking {} because of re-sync needed", player);
player.disconnect(Component.text("Please reconnect"));
reSyncMap.put(player.getUsername(),forgeServerConnection);
}
@Subscribe
public void onServerConnectedEvent(ServerConnectedEvent event, Continuation continuation) {
ConnectedPlayer player = ((ConnectedPlayer) event.getPlayer());
Optional<ForgeServerConnection> forgeServerConnection = ambassador.forgeHandshakeHandler.getForgeServerConnection(event.getServer());
Optional<ForgeConnection> forgeConnection = ambassador.forgeHandshakeHandler.getForgeConnection(player);
if (forgeConnection.isPresent() && forgeServerConnection.isPresent() && event.getPreviousServer().isPresent()) {
Future<ForgeHandshakeUtils.CachedServerHandshake> handshakeFuture = forgeServerConnection.get().getHandshake();
player.getConnection().eventLoop().submit(() -> {
reSync(player,handshakeFuture,continuation);
});
} else {
continuation.resume();
}
}
private void reSync(ConnectedPlayer player, Future<ForgeHandshakeUtils.CachedServerHandshake> handshakeFuture, Continuation continuation) {
MinecraftConnection connection = player.getConnection();
connection.setSessionHandler(new ReSyncHandler(player,handshakeFuture,continuation));
connection.write(new PluginMessage("fml:handshake", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket())));
connection.setState(StateRegistry.LOGIN);
}
private class ReSyncHandler implements MinecraftSessionHandler {
private final Player player;
private final MinecraftConnection connection;
private final Future<ForgeHandshakeUtils.CachedServerHandshake> handshakeFuture;
private final MinecraftSessionHandler originalHandler;
private final Continuation continuation;
private int sent = 0;
ReSyncHandler(ConnectedPlayer player, Future<ForgeHandshakeUtils.CachedServerHandshake> handshakeFuture, Continuation continuation) {
this.player = player;
this.connection = player.getConnection();
this.handshakeFuture = handshakeFuture;
this.originalHandler = this.connection.getSessionHandler();
this.continuation = continuation;
}
@Override
public boolean handle(LoginPluginResponse packet) {
if (sent == 0) {
ForgeHandshakeUtils.CachedServerHandshake handshake;
try {
handshake = handshakeFuture.get();
} catch (Exception e) {
return true;
}
sent = sendHandshake(connection, handshake);
} else {
if (sent == 1) {
complete();
}
sent--;
}
return true;
}
private int sendHandshake(MinecraftConnection connection, ForgeHandshakeUtils.CachedServerHandshake handshake) {
int transactionId = 1;
connection.delayedWrite(new LoginPluginMessage(transactionId, "fml:loginwrapper", Unpooled.wrappedBuffer(handshake.modListPacket)));
for (byte[] data : handshake.otherPackets) {
transactionId++;
connection.delayedWrite(new LoginPluginMessage(transactionId, "fml:loginwrapper", Unpooled.wrappedBuffer(data)));
}
connection.flush();
return transactionId;
}
private void complete() {
VelocityConfiguration configuration = (VelocityConfiguration) ambassador.server.getConfiguration();
UUID playerUniqueId = player.getUniqueId();
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.NONE) {
playerUniqueId = UuidUtils.generateOfflinePlayerUuid(player.getUsername());
}
ServerLoginSuccess success = new ServerLoginSuccess();
success.setUsername(player.getUsername());
success.setUuid(playerUniqueId);
connection.write(success);
connection.setState(StateRegistry.PLAY);
connection.setSessionHandler(originalHandler);
continuation.resume();
}
}
}