diff --git a/src/main/java/org/adde0109/ambassador/Ambassador.java b/src/main/java/org/adde0109/ambassador/Ambassador.java index 1616806..7f98543 100644 --- a/src/main/java/org/adde0109/ambassador/Ambassador.java +++ b/src/main/java/org/adde0109/ambassador/Ambassador.java @@ -1,11 +1,8 @@ package org.adde0109.ambassador; import com.google.inject.Inject; -import com.velocitypowered.api.event.Continuation; import com.velocitypowered.api.event.Subscribe; -import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; -import com.velocitypowered.api.event.proxy.ProxyReloadEvent; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.ProxyServer; @@ -16,10 +13,6 @@ import java.util.concurrent.Callable; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.network.ConnectionManager; import io.netty.channel.ChannelInitializer; -import org.adde0109.ambassador.forge.ForgeConnection; -import org.adde0109.ambassador.forge.ForgeHandshakeHandler; -import org.adde0109.ambassador.forge.ForgeHandshakeUtils; -import org.adde0109.ambassador.forge.ForgeServerSwitchHandler; import org.adde0109.ambassador.velocity.VelocityBackendChannelInitializer; import org.adde0109.ambassador.velocity.VelocityServerChannelInitializer; import org.adde0109.ambassador.velocity.VelocityEventHandler; @@ -34,12 +27,9 @@ public class Ambassador { public ProxyServer server; public final Logger logger; - public AmbassadorConfig config; private final Metrics.Factory metricsFactory; private final Path dataDirectory; - public ForgeHandshakeHandler forgeHandshakeHandler; - public ForgeServerSwitchHandler forgeServerSwitchHandler; @@ -55,18 +45,8 @@ public class Ambassador { public void onProxyInitialization(ProxyInitializeEvent event) throws ReflectiveOperationException { initMetrics(); - config = AmbassadorConfig.readOrCreateConfig(dataDirectory,server,logger); - if(config != null) { - forgeHandshakeHandler = new ForgeHandshakeHandler(this); - forgeServerSwitchHandler = new ForgeServerSwitchHandler(this); - server.getEventManager().register(this, new VelocityEventHandler(this)); - server.getEventManager().register(this,forgeServerSwitchHandler); - } - else { - logger.warn("Ambassador will be disabled because of errors"); - } + server.getEventManager().register(this, new VelocityEventHandler(this)); - ForgeHandshakeUtils.HandshakeReceiver.logger = logger; inject(); } @@ -81,39 +61,7 @@ public class Ambassador { ((ConnectionManager) cmField.get(server)).backendChannelInitializer.set(new VelocityBackendChannelInitializer(originalBackend,(VelocityServer) server)); } - @Subscribe - public void onProxyReloadEvent(ProxyReloadEvent event) { - AmbassadorConfig c = AmbassadorConfig.readOrCreateConfig(dataDirectory,server,logger); - if (config != null) { - config = c; - logger.info("Successfully reloaded the config"); - } else { - logger.warn("Using the old config"); - } - } - - - @Subscribe - public void onPlayerChooseInitialServerEvent(PlayerChooseInitialServerEvent event, Continuation continuation) { - //Only handle Forge connections - if((event.getInitialServer().isPresent()) && (forgeHandshakeHandler.getForgeConnection(event.getPlayer()).isPresent())) { - //Forge client - ForgeConnection forgeConnection = forgeHandshakeHandler.getForgeConnection(event.getPlayer()).get(); - if (forgeConnection.isForced()) { - event.setInitialServer(forgeConnection.getSyncedServer().get()); - } - forgeConnection.setForced(config.getForced(forgeConnection.getConnection().getProtocolVersion().getProtocol())); - } - continuation.resume(); - } - private void initMetrics() { Metrics metrics = metricsFactory.make(this, 15655); - metrics.addCustomChart(new SingleLineChart("modern_forge_players", new Callable() { - @Override - public Integer call() throws Exception { - return (forgeHandshakeHandler != null) ? forgeHandshakeHandler.getAmountOfForgeConnections() : 0; - } - })); } } diff --git a/src/main/java/org/adde0109/ambassador/AmbassadorConfig.java b/src/main/java/org/adde0109/ambassador/AmbassadorConfig.java deleted file mode 100644 index b119392..0000000 --- a/src/main/java/org/adde0109/ambassador/AmbassadorConfig.java +++ /dev/null @@ -1,164 +0,0 @@ -package org.adde0109.ambassador; - -import com.electronwill.nightconfig.core.CommentedConfig; -import com.electronwill.nightconfig.core.UnmodifiableConfig; -import com.electronwill.nightconfig.core.file.CommentedFileConfig; -import com.google.common.collect.ImmutableMap; -import com.velocitypowered.api.proxy.ProxyServer; -import com.velocitypowered.api.proxy.server.RegisteredServer; -import org.slf4j.Logger; - -import java.io.IOException; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -public class AmbassadorConfig { - - private static final int CONFIG_VERSION = 1; - private final ProxyServer server; - private final Logger logger; - private Differentiators differentiatorsSettings; - private ReSync reSyncSettings; - - - private AmbassadorConfig(ProxyServer server,Logger logger) { - this.server = server; - this.logger = logger; - } - - - - public RegisteredServer getServer(int protocolVersion) { - return differentiatorsSettings.differentiators.get(protocolVersion).handshakeServer; - } - - public boolean getForced (int protocolVersion) { - return differentiatorsSettings.differentiators.get(protocolVersion).forced; - } - - public boolean shouldHandle(int protocolVersion) { - return differentiatorsSettings.differentiators.containsKey(protocolVersion); - } - - public int getReSyncTimeout() { - return reSyncSettings.reSyncTimeout; - } - - public reSyncOption reSyncOptionForge() { - return reSyncSettings.reSyncForgeForge; - } - - public reSyncOption reSyncOptionVanilla() { - return reSyncSettings.reSyncForgeVanilla; - } - - - public static AmbassadorConfig readOrCreateConfig(Path dataDirectory,ProxyServer server, Logger logger) { - AmbassadorConfig ambassadorConfig = new AmbassadorConfig(server,logger); - try { - Files.createDirectories(dataDirectory); - - } - catch (FileAlreadyExistsException ignored) { - - } - catch (IOException e) { - logger.error("Config related error: " + e); - return null; - } - - try { - CommentedFileConfig config = CommentedFileConfig.builder(dataDirectory.resolve("ambassador.toml")) - .defaultData(Ambassador.class.getClassLoader().getResource("default-ambassador.toml")) - .autosave() - .preserveInsertionOrder() - .sync() - .build(); - config.load(); - - if (config.getOrElse("config-version",0) != CONFIG_VERSION) { - throw new Exception("Incompatible config-version detected! Please delete 'ambassador.toml' and reload."); - } - CommentedConfig differentiatorsSettingsConfig = config.get("Differentiators"); - CommentedConfig reSyncSettingsConfig = config.get("ReSync"); - - - ambassadorConfig.differentiatorsSettings = ambassadorConfig.new Differentiators(differentiatorsSettingsConfig); - ambassadorConfig.reSyncSettings = ambassadorConfig.new ReSync(reSyncSettingsConfig); - - - config.save(); - return ambassadorConfig; - } - catch (Exception e) { - logger.error("Config related error: " + e); - return null; - } - } - - private class ReSync { - private int reSyncTimeout = 30; - private reSyncOption reSyncForgeForge = reSyncOption.ALWAYS; - private reSyncOption reSyncForgeVanilla = reSyncOption.NEVER; - - private ReSync(CommentedConfig config) { - if (config != null) { - reSyncTimeout = config.getOrElse("resync-timeout",reSyncTimeout); - reSyncForgeForge = reSyncOption.valueOf( - config.getOrElse("resync-forge-to-forge",reSyncForgeForge.name()).toUpperCase()); - } - reSyncForgeVanilla = reSyncOption.valueOf( - config.getOrElse("unsync-forge-to-vanilla",reSyncForgeVanilla.name()).toUpperCase()); - } - } - - public enum reSyncOption { - NEVER, - ASK, - ALWAYS - } - - private class Differentiators { - private Map differentiators = ImmutableMap.of( - 758, new DifferentiatorSettings(), - 754, new DifferentiatorSettings() - ); - private Differentiators(){ - } - - private Differentiators(CommentedConfig config) throws Exception { - if (config != null) { - Map differentiators = new HashMap<>(); - for (UnmodifiableConfig.Entry entry : config.entrySet()) { - if (entry.getValue() instanceof CommentedConfig) { - differentiators.put(Integer.decode(entry.getKey()),new DifferentiatorSettings(entry.getValue())); - } - } - this.differentiators = ImmutableMap.copyOf(differentiators); - } - } - } - - private class DifferentiatorSettings { - private RegisteredServer handshakeServer = null; - private boolean forced = false; - - private DifferentiatorSettings(){ - } - - private DifferentiatorSettings(CommentedConfig config) throws Exception { - if (config != null) { - String serverName = config.getOrElse("default-forge-server", ""); - if (!Objects.equals(serverName, "")) - handshakeServer = server.getServer(serverName) - .orElseThrow(() -> new Exception(serverName + "is not a registered server!")); - this.forced = config.getOrElse("forced",forced); - } - } - - } -} diff --git a/src/main/java/org/adde0109/ambassador/forge/ForgeConnection.java b/src/main/java/org/adde0109/ambassador/forge/ForgeConnection.java deleted file mode 100644 index 3707c9c..0000000 --- a/src/main/java/org/adde0109/ambassador/forge/ForgeConnection.java +++ /dev/null @@ -1,168 +0,0 @@ -package org.adde0109.ambassador.forge; - -import com.google.common.io.ByteArrayDataInput; -import com.velocitypowered.api.event.Continuation; -import com.velocitypowered.api.event.player.ServerLoginPluginMessageEvent; -import com.velocitypowered.api.proxy.LoginPhaseConnection; -import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; -import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.proxy.protocol.ProtocolUtils; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import org.slf4j.Logger; - -import java.io.EOFException; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; - -public class ForgeConnection { - - private final Logger logger; - private final LoginPhaseConnection connection; - - private Optional recivedClientModlist = Optional.empty(); - private static byte[] recivedClientACK; - private boolean ignoreSyncExepction = false; - - private Optional transmittedHandshake = Optional.empty(); - private boolean forced = false; - private Optional syncedTo = Optional.empty(); - - private boolean resettable; - - public ForgeConnection(LoginPhaseConnection connection, Logger logger) { - this.connection = connection; - this.logger = logger; - } - - public CompletableFuture testIfForge(LoginPhaseConnection connection) { - CompletableFuture future = new CompletableFuture<>(); - - byte[] testPacket = ForgeHandshakeUtils.generateTestPacket(); - //This gets also sent to vanilla - connection.sendLoginPluginMessage(MinecraftChannelIdentifier.create("fml", "loginwrapper"), testPacket, - responseBody -> { - future.complete(responseBody != null); - ignoreSyncExepction = responseBody == null; - }); - return future; - } - - - - public CompletableFuture sync(ForgeServerConnection forgeServerConnection) { - CompletableFuture future = new CompletableFuture<>(); - forgeServerConnection.getHandshake().whenComplete((msg,ex) -> { - if (ex != null) { - future.complete(false); - logger.warn("Sync Exception: " + ex); - } else { - //This gets also sent to vanilla - sendModlist(msg.modListPacket).thenAccept((response) -> { - if (!ignoreSyncExepction && response == null) { - logger.warn("Sync Exception: Client responded with an empty body."); - } - if (response != null) { - recivedClientModlist = Optional.of(response); - //If the client is resettable. - ByteBuf clientModListPacket = Unpooled.wrappedBuffer(response); - clientModListPacket.readBytes(14); //Channel Identifier - ProtocolUtils.readVarInt(clientModListPacket); //Length - int packetID = ProtocolUtils.readVarInt(clientModListPacket); - String[] mods = ProtocolUtils.readStringArray(clientModListPacket); - resettable = Arrays.stream(mods).anyMatch((s) -> s.equals("clientresetpacket")); - clientModListPacket.release(); - } - }); - //This gets also sent to vanilla - sendOther(msg.otherPackets).thenAccept((response) -> { - if (!ignoreSyncExepction && response == null) { - logger.warn("Sync Exception: Client responded with an empty body."); - } - //TODO: Generate the ACK packet ourself. - ForgeConnection.recivedClientACK = (response == null) ? ForgeConnection.recivedClientACK : response; - transmittedHandshake = Optional.of(msg); - syncedTo = Optional.of(forgeServerConnection.getServer()); - }); - future.complete(true); - } - }); - return future; - } - - private CompletableFuture sendModlist(byte[] modListPacket) { - CompletableFuture future = new CompletableFuture<>(); - connection.sendLoginPluginMessage(MinecraftChannelIdentifier.create("fml", "loginwrapper"), modListPacket, - future::complete); - return future; - } - - private CompletableFuture sendOther(List otherPackets) { - CompletableFuture future = new CompletableFuture<>(); - for (int i = 0; i < otherPackets.size(); i++) { - connection.sendLoginPluginMessage(MinecraftChannelIdentifier.create("fml", "loginwrapper"), otherPackets.get(i), - (i < (otherPackets.size() - 1)) ? responseBody -> { - } : future::complete); - } - return future; - } - - public void handleServerHandshakePacket(ServerLoginPluginMessageEvent event, Continuation continuation) { - ByteArrayDataInput data = event.contentsAsDataStream(); - if (data.skipBytes(14) != 14) { //Channel Identifier - continuation.resumeWithException(new EOFException()); - return; - } - ForgeHandshakeUtils.readVarInt(data); //Length - int packetID = ForgeHandshakeUtils.readVarInt(data); - - if (packetID == 1) { - if (getRecivedClientModlist().isPresent()) { - event.setResult(ServerLoginPluginMessageEvent.ResponseResult.reply(getRecivedClientModlist().get())); - } else { - continuation.resumeWithException(new Exception("Client isn't synced. This should have been caught" + - " during serverPreConnect")); - return; - } - } else { - if (getRecivedClientACK() != null) { - event.setResult(ServerLoginPluginMessageEvent.ResponseResult.reply(getRecivedClientACK())); - } else { - continuation.resumeWithException(new Exception("No response available.")); - return; - } - } - continuation.resume(); -} - - public LoginPhaseConnection getConnection() { - return connection; - } - - public boolean isResettable() { - return resettable; - } - - public Optional getTransmittedHandshake() { - return transmittedHandshake; - } - - public Optional getRecivedClientModlist() { - return recivedClientModlist; - } - - public static byte[] getRecivedClientACK() { - return recivedClientACK; - } - public Optional getSyncedServer() { - return syncedTo; - } - public void setForced(boolean forced) { - this.forced = forced; - } - public boolean isForced() { - return forced; - } -} diff --git a/src/main/java/org/adde0109/ambassador/forge/ForgeFML2ClientConnectionPhase.java b/src/main/java/org/adde0109/ambassador/forge/ForgeFML2ClientConnectionPhase.java index 4fabb3a..521ad7a 100644 --- a/src/main/java/org/adde0109/ambassador/forge/ForgeFML2ClientConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/forge/ForgeFML2ClientConnectionPhase.java @@ -19,6 +19,7 @@ import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; +import net.kyori.adventure.text.Component; import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase; import org.adde0109.ambassador.velocity.VelocityForgeHandshakeSessionHandler; import org.checkerframework.checker.units.qual.A; @@ -45,13 +46,10 @@ public class ForgeFML2ClientConnectionPhase implements VelocityForgeClientConnec public void handleLogin(ConnectedPlayer player,ForgeHandshakeUtils.CachedServerHandshake handshake, Continuation continuation) { final MinecraftConnection connection = player.getConnection(); VelocityForgeHandshakeSessionHandler sessionHandler = new VelocityForgeHandshakeSessionHandler(connection.getSessionHandler(), player); - if (handshake == null) { - connection.delayedWrite(new LoginPluginMessage(98, "fml:loginwrapper", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket()))); - listenerList.add(98); - this.whenComplete = () -> { - this.clientPhase = ClientPhase.VANILLA; - continuation.resume(); - }; + //Without initial modlist for now + if (true) { + connection.delayedWrite(new LoginPluginMessage(0, "fml:loginwrapper", Unpooled.wrappedBuffer(ForgeHandshakeUtils.emptyModlist))); + listenerList.add(0); } else { connection.delayedWrite(new LoginPluginMessage(0, "fml:loginwrapper", Unpooled.wrappedBuffer(handshake.modListPacket))); listenerList.add(0); @@ -59,11 +57,11 @@ public class ForgeFML2ClientConnectionPhase implements VelocityForgeClientConnec connection.delayedWrite(new LoginPluginMessage(i + 1, "fml:loginwrapper", Unpooled.wrappedBuffer(handshake.otherPackets.get(i)))); listenerList.add(i + 1); } - this.whenComplete = () -> { - this.clientPhase = ClientPhase.MODDED; - continuation.resume(); - }; } + this.whenComplete = () -> { + this.clientPhase = ClientPhase.MODDED; + continuation.resume(); + }; connection.setSessionHandler(sessionHandler); connection.flush(); } @@ -72,9 +70,11 @@ public class ForgeFML2ClientConnectionPhase implements VelocityForgeClientConnec public boolean handle(ConnectedPlayer player, LoginPluginResponse packet) { if (packet.getId() == 98) { this.clientPhase = ClientPhase.HANDSHAKE; - this.isResettable = packet.isSuccess(); } else if (packet.getId() == 0) { this.clientPhase = ClientPhase.MODLIST; + if (modListData == null) { + modListData = ByteBufUtil.getBytes(packet.content()); + } } if (!listenerList.removeIf(id -> id.equals(packet.getId()))) { player.getConnectionInFlight().getConnection().write(packet.retain()); diff --git a/src/main/java/org/adde0109/ambassador/forge/ForgeHandshakeHandler.java b/src/main/java/org/adde0109/ambassador/forge/ForgeHandshakeHandler.java deleted file mode 100644 index 1019aee..0000000 --- a/src/main/java/org/adde0109/ambassador/forge/ForgeHandshakeHandler.java +++ /dev/null @@ -1,144 +0,0 @@ -package org.adde0109.ambassador.forge; - -import com.velocitypowered.api.event.Continuation; -import com.velocitypowered.api.event.Subscribe; -import com.velocitypowered.api.event.player.KickedFromServerEvent; -import com.velocitypowered.api.event.player.ServerLoginPluginMessageEvent; -import com.velocitypowered.api.proxy.Player; -import com.velocitypowered.api.proxy.messages.ChannelIdentifier; -import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; -import com.velocitypowered.api.proxy.server.RegisteredServer; -import java.net.InetSocketAddress; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; - -import com.velocitypowered.proxy.connection.client.ConnectedPlayer; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TranslatableComponent; -import net.kyori.adventure.text.format.NamedTextColor; -import org.adde0109.ambassador.Ambassador; -import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase; - -public class ForgeHandshakeHandler { - - private final Ambassador ambassador; - - private final Map - forgeServerConnectionMap = new HashMap<>(); - private final Map incomingForgeConnections = new HashMap<>(); - - private static final ChannelIdentifier LOGIN_WRAPPER_ID = MinecraftChannelIdentifier.create("fml","loginwrapper"); - - - public ForgeHandshakeHandler(Ambassador ambassador) { - this.ambassador = ambassador; - } - - - - public void handleLogin(ConnectedPlayer player, Continuation continuation) { - getInitialHandshake(player).whenCompleteAsync((msg,ex) -> { - if (ex != null) { - ambassador.logger.warn("Forge player, " + player.getUsername() + ", is entering vanilla-mode because of: " + ex.getMessage()); - } - ((VelocityForgeClientConnectionPhase) player.getPhase()).handleLogin(player,msg,continuation); - }, player.getConnection().eventLoop()); - } - - private CompletableFuture getInitialHandshake(ConnectedPlayer player) { - CompletableFuture future; - RegisteredServer initialServer; - if((initialServer = ambassador.config.getServer(player.getConnection().getProtocolVersion().getProtocol())) != null) { - future = ForgeHandshakeUtils.HandshakeReceiver.downloadHandshake(initialServer); - } else { - future = CompletableFuture.failedFuture(new Exception("No initial server is specified")); - } - return future; - } - - private void registerForgeConnection(ForgeConnection forgeConnection) { - if (forgeConnection != null) { - incomingForgeConnections.values().removeIf((c) -> !c.getConnection().isActive()); - incomingForgeConnections.put(forgeConnection.getConnection().getRemoteAddress(), forgeConnection); - } - } - - public Optional getForgeConnection(Player player) { - return getForgeConnection(player.getRemoteAddress()); - } - - private Optional getForgeConnection(InetSocketAddress socketAddress) { - incomingForgeConnections.values().removeIf((c) -> !c.getConnection().isActive()); - return Optional.ofNullable(incomingForgeConnections.get(socketAddress)); - } - - public int getAmountOfForgeConnections() { - return incomingForgeConnections.size(); - } - - public Optional getForgeServerConnection(RegisteredServer registeredServer) { - return Optional.ofNullable(forgeServerConnectionMap.get(registeredServer)); - } - - public void registerForgeServer(RegisteredServer server, ForgeServerConnection forgeServerConnection) { - forgeServerConnectionMap.put(server,forgeServerConnection); - } - public void unRegisterForgeServer(RegisteredServer server) { - forgeServerConnectionMap.remove(server); - } - - //@Subscribe - public void onServerLoginPluginMessageEvent(ServerLoginPluginMessageEvent event, Continuation continuation) { - if (!event.getIdentifier().equals(LOGIN_WRAPPER_ID)) { - continuation.resume(); - return; - } - //Check 2 - if (getForgeServerConnection(event.getConnection().getServer()).isEmpty()) { - registerForgeServer(event.getConnection().getServer(), - new ForgeServerConnection(event.getConnection().getServer())); - } - - if (incomingForgeConnections.containsKey(event.getConnection().getPlayer().getRemoteAddress())) { - incomingForgeConnections.get(event.getConnection().getPlayer().getRemoteAddress()) - .handleServerHandshakePacket(event,continuation); - } else { - //This will lead to "multiplayer.disconnect.unexpected_query_response" - //and will be handled during KickFromServerEvent. - continuation.resume(); - } - } - - //@Subscribe - public void onKickedFromServerEvent(KickedFromServerEvent event, Continuation continuation) { - Optional forgeConnectionOptional = getForgeConnection(event.getPlayer()); - if (forgeConnectionOptional.isPresent()) { - if (forgeConnectionOptional.get().isForced() && event.getResult() instanceof KickedFromServerEvent.RedirectPlayer) { - event.setResult(KickedFromServerEvent.DisconnectPlayer.create(event.getServerKickReason().get())); - } - } else if (event.getServerKickReason().isPresent()) { - Component reason = event.getServerKickReason().get(); - if (reason instanceof TranslatableComponent) - if (((TranslatableComponent) reason).key().equals("multiplayer.disconnect.unexpected_query_response")) { - if (getForgeServerConnection(event.getServer()).isPresent()) { - //Turns out the server the vanilla client is connecting to is forge. Let's handle the connection error. - ambassador.logger.info("Vanilla player {} tried to connect to forge server {}. The connection error can be ignored.", - event.getPlayer(),event.getServer().getServerInfo().getName()); - KickedFromServerEvent.ServerKickResult result = event.getResult(); - Component component = Component.text("The server you were trying to connect to requires Forge to be installed.", NamedTextColor.RED); - if (result instanceof KickedFromServerEvent.DisconnectPlayer) { - event.setResult(KickedFromServerEvent.DisconnectPlayer.create(component)); - } else if (result instanceof KickedFromServerEvent.RedirectPlayer) { - RegisteredServer redirectServer = ((KickedFromServerEvent.RedirectPlayer)event.getResult()).getServer(); - event.setResult(KickedFromServerEvent.RedirectPlayer.create(redirectServer,component)); - } else if (result instanceof KickedFromServerEvent.Notify) { - event.setResult(KickedFromServerEvent.Notify.create(component)); - } - } - } - } - continuation.resume(); - } -} diff --git a/src/main/java/org/adde0109/ambassador/forge/ForgeServerConnection.java b/src/main/java/org/adde0109/ambassador/forge/ForgeServerConnection.java deleted file mode 100644 index e8f6fcf..0000000 --- a/src/main/java/org/adde0109/ambassador/forge/ForgeServerConnection.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.adde0109.ambassador.forge; - -import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.api.proxy.server.ServerInfo; -import java.util.concurrent.CompletableFuture; - -public class ForgeServerConnection { - - private static final int PACKET_LENGTH_INDEX = 14; //length of "fml:handshake"+1 - private final RegisteredServer handshakeServer; - private ForgeHandshakeUtils.CachedServerHandshake handshake; - - public RegisteredServer getServer() { - return handshakeServer; - } - - public ForgeServerConnection(RegisteredServer handshakeServer) { - this.handshakeServer = handshakeServer; - } - - public CompletableFuture getHandshake() { - CompletableFuture future; - if (handshake == null) { - future = ForgeHandshakeUtils.HandshakeReceiver.downloadHandshake(handshakeServer); - } else { - future = ForgeHandshakeUtils.HandshakeReceiver.downloadHandshake(handshakeServer,handshake); - } - future.thenAccept(p -> handshake = p); - return future; - } - - public ServerInfo getServerInfo() { - return handshakeServer.getServerInfo(); - } - -} diff --git a/src/main/java/org/adde0109/ambassador/forge/ForgeServerSwitchHandler.java b/src/main/java/org/adde0109/ambassador/forge/ForgeServerSwitchHandler.java deleted file mode 100644 index 06c788a..0000000 --- a/src/main/java/org/adde0109/ambassador/forge/ForgeServerSwitchHandler.java +++ /dev/null @@ -1,217 +0,0 @@ -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.*; - -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.ByteBuf; -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; - -import org.adde0109.ambassador.AmbassadorConfig; -import org.apache.commons.collections4.map.PassiveExpiringMap; - -public class ForgeServerSwitchHandler { - private final Ambassador ambassador; - public final PassiveExpiringMap reSyncMap; - - public ForgeServerSwitchHandler(Ambassador ambassador) { - this.ambassador = ambassador; - this.reSyncMap = new PassiveExpiringMap<>(ambassador.config.getReSyncTimeout(),TimeUnit.SECONDS); - } - - - //@Subscribe(order = PostOrder.LAST) - public void onServerPreConnectEvent(ServerPreConnectEvent event, Continuation continuation) { - if (!event.getResult().isAllowed()) { - continuation.resume(); - return; - } - Optional forgeServerConnectionOptional = ambassador.forgeHandshakeHandler.getForgeServerConnection(event.getOriginalServer()); - Optional forgeConnectionOptional = ambassador.forgeHandshakeHandler.getForgeConnection(event.getPlayer()); - if (forgeConnectionOptional.isPresent()) { - ForgeConnection forgeConnection = forgeConnectionOptional.get(); - if (forgeConnection.isResettable()) { - continuation.resume(); - return; - } - ForgeServerConnection forgeServerConnection = forgeServerConnectionOptional.orElseGet(() -> new ForgeServerConnection(event.getOriginalServer())); - forgeServerConnection.getHandshake().whenComplete((msg, ex) -> { - if (ex != null) { - //The server was forge but aren't right now. Or it's just offline. - if (ex instanceof ForgeHandshakeUtils.HandshakeReceiver.HandshakeNotAvailableException) { - //It's not running ambassador, so it should be unregistered. - if (forgeServerConnectionOptional.isPresent()) - ambassador.forgeHandshakeHandler.unRegisterForgeServer(forgeServerConnection.getServer()); - } - } else { - //If the server just got discovered, register it. - if (forgeServerConnectionOptional.isEmpty()) - ambassador.forgeHandshakeHandler.registerForgeServer(event.getOriginalServer(),forgeServerConnection); - - //To make legacy forwarding work - List properties = new ArrayList<>(event.getPlayer().getGameProfileProperties()); - properties.add(new GameProfile.Property("extraData", "\1FML2\1","")); - event.getPlayer().setGameProfileProperties(properties); - - if (ambassador.config.reSyncOptionForge() != AmbassadorConfig.reSyncOption.NEVER) { - if (forgeConnection.getTransmittedHandshake().isEmpty() || !msg.equals(forgeConnection.getTransmittedHandshake().get())) { - event.setResult(ServerPreConnectEvent.ServerResult.denied()); - kickReSync(event.getPlayer(), forgeServerConnection); - } - } - } - continuation.resume(); - }); - } else if (forgeServerConnectionOptional.isPresent()) { - //If vanilla tries to connect to a server we know is forge - event.setResult(ServerPreConnectEvent.ServerResult.denied()); - event.getPlayer().sendMessage(Component.text("This server requires Forge!", NamedTextColor.RED)); - continuation.resume(); - } else { - //The server is not known to us. - continuation.resume(); - } - } - 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 forgeConnection = ambassador.forgeHandshakeHandler.getForgeConnection(player); - if (forgeConnection.isEmpty() || !forgeConnection.get().isResettable()) { - //Don't bother unless the client can be reset. - continuation.resume(); - return; - } - Optional forgeServerConnection = ambassador.forgeHandshakeHandler.getForgeServerConnection(event.getServer()); - if (forgeServerConnection.isPresent() && event.getPreviousServer().isPresent()) { - Future handshakeFuture = forgeServerConnection.get().getHandshake(); - player.getConnection().eventLoop().submit(() -> { - reSync(player,handshakeFuture,continuation); - }); - } else { - continuation.resume(); - } - } - - private void reSync(ConnectedPlayer player, Future 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 ConnectedPlayer player; - - private final MinecraftConnection connection; - private final Future handshakeFuture; - private final MinecraftSessionHandler originalHandler; - private final Continuation continuation; - - private List inTransit = new ArrayList<>(); - - ReSyncHandler(ConnectedPlayer player, Future 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 (!inTransit.removeIf((s) -> s == packet.getId())) { - if (packet.getId() == 98) { - ForgeHandshakeUtils.CachedServerHandshake handshake; - try { - handshake = handshakeFuture.get(); - } catch (Exception e) { - return true; - } - sendHandshake(connection, handshake); - return true; - } - return false; - } else if (inTransit.isEmpty()) { - complete(); - } - return true; - } - - private void sendHandshake(MinecraftConnection connection, ForgeHandshakeUtils.CachedServerHandshake handshake) { - int transactionId = 1; - connection.delayedWrite(new LoginPluginMessage(transactionId, "fml:loginwrapper", Unpooled.wrappedBuffer(handshake.modListPacket))); - inTransit.add(transactionId); - for (byte[] data : handshake.otherPackets) { - transactionId++; - connection.delayedWrite(new LoginPluginMessage(transactionId, "fml:loginwrapper", Unpooled.wrappedBuffer(data))); - inTransit.add(transactionId); - } - connection.flush(); - } - - 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(); - } - - @Override - public void handleUnknown(ByteBuf buf) { - originalHandler.handleUnknown(buf); - } - - @Override - public void disconnected() { - originalHandler.disconnected(); - } - } -} diff --git a/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java b/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java index 7edb4c7..7a23b64 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java +++ b/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java @@ -36,7 +36,7 @@ public class VelocityEventHandler { continuation.resume(); return; } - ambassador.forgeHandshakeHandler.handleLogin(player,continuation); + phase.handleLogin(player,null,continuation); } /*@Subscribe diff --git a/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendConnectionPhase.java b/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendConnectionPhase.java index bfc7a22..df8a982 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendConnectionPhase.java @@ -13,7 +13,7 @@ import java.util.List; public class VelocityForgeBackendConnectionPhase implements BackendConnectionPhase { - private final List queuedHandshakePackets = new ArrayList<>(); + private List queuedHandshakePackets = new ArrayList<>(); public VelocityForgeBackendConnectionPhase() { } @@ -34,7 +34,9 @@ public class VelocityForgeBackendConnectionPhase implements BackendConnectionPha player.getConnection().delayedWrite(msg); } player.getConnection().flush(); + queuedHandshakePackets = null; }); + queuedHandshakePackets = new ArrayList<>(); queuedHandshakePackets.add(message); } else if (clientPhase.clientPhase != null) { player.getConnection().write(message); diff --git a/src/main/resources/default-ambassador.toml b/src/main/resources/default-ambassador.toml deleted file mode 100644 index ef41372..0000000 --- a/src/main/resources/default-ambassador.toml +++ /dev/null @@ -1,39 +0,0 @@ -config-version = 1 -# The player can only be synced to ONE forge server at a time. -# Resync will always happen if the player isn't synced to any server and is switching to a forge server. -[ReSync] -# In seconds. How much time the player has to reconnect before the resync cancels. -resync-timeout = 30 - -# Possible values: "Always", "Never". -# Always: Always kicks the player while switching servers. The safest. -# Never: Switches server without kicking/resyncing the player. Can cause client crashes depending on the mods. - -# When the player is connecting to a forge server while being synced to another. -# Default: "always" -resync-forge-to-forge = "always" -# When the player is connecting to a vanilla server and is synced to a forge server. -# Default "never". "always" not recommended for servers with vanilla hubs/lobbies. -unsync-forge-to-vanilla = "never" - -# Maybe you want to have one 1.16.5 modpack-server and one 1.18.2 modpack-server behind Velocity, in order for Ambassador to tell the -# diffrence between modpacks on the connecting client, the plugin looks at the client's protocol version. - -# You may add more diffrentiators, just make sure they have diffrent protocol versions. -[Differentiators] - -# Protocol version - 1.18.2 -[Differentiators.758] -# The server that players will initially sync to when connecting to the proxy. -# When left empty; Players will be able to join but will need to resync when joining connecting to a forge server. -# The name should be the same as specified in the "velocity.toml" config, e.g. "lobby". -default-forge-server = "" - -# Some modpacks are not compatible with vanilla servers. -# To make sure they don't get connected to one upon login (like a vanilla lobby), change this to true. -forced = false - -# Protocol version - 1.16.5 -[Differentiators.754] -default-forge-server = "" -forced = false \ No newline at end of file