diff --git a/build.gradle.kts b/build.gradle.kts index 9e9ca54..7abec89 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } group = "org.adde0109" -version = "1.5.2-beta" +version = "1.5.3-beta" repositories { mavenCentral() diff --git a/src/main/java/org/adde0109/ambassador/Ambassador.java b/src/main/java/org/adde0109/ambassador/Ambassador.java index 1fe22dd..d3748fd 100644 --- a/src/main/java/org/adde0109/ambassador/Ambassador.java +++ b/src/main/java/org/adde0109/ambassador/Ambassador.java @@ -19,6 +19,7 @@ import java.util.Map; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; +import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.network.ConnectionManager; import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.packet.DisconnectPacket; @@ -28,6 +29,8 @@ import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentPropertySeria import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.adde0109.ambassador.velocity.VelocityBackendChannelInitializer; import org.adde0109.ambassador.velocity.VelocityServerChannelInitializer; import org.adde0109.ambassador.velocity.VelocityEventHandler; @@ -43,7 +46,7 @@ import java.util.concurrent.TimeUnit; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19; import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.mapSet; -@Plugin(id = "ambassador", name = "Ambassador", version = "1.5.2-beta", authors = {"adde0109"}) +@Plugin(id = "ambassador", name = "Ambassador", version = "1.5.3-beta", authors = {"adde0109"}) public class Ambassador { //Don't forget to update checkCompatibleVersion() when changing this value @@ -153,6 +156,16 @@ public class Ambassador { return TEMPORARY_FORCED; } + public void reconnectSwitchPlayer(ConnectedPlayer player) { + TEMPORARY_FORCED.put(player.getUsername(), player.getConnectionInFlight().getServer(), + config.getServerSwitchCancellationTime(), TimeUnit.SECONDS); + + MiniMessage mm = MiniMessage.miniMessage(); + Component parsed = mm.deserialize(config.getKickReconnectMessageString()); + + player.disconnect(parsed); + } + private void initMetrics() { Metrics metrics = metricsFactory.make(this, 15655); } diff --git a/src/main/java/org/adde0109/ambassador/AmbassadorConfig.java b/src/main/java/org/adde0109/ambassador/AmbassadorConfig.java index eca6c0e..a1a517d 100644 --- a/src/main/java/org/adde0109/ambassador/AmbassadorConfig.java +++ b/src/main/java/org/adde0109/ambassador/AmbassadorConfig.java @@ -19,7 +19,6 @@ public class AmbassadorConfig { @Expose private boolean silenceWarnings = false; - @Expose private boolean bypassRegistryCheck = false; @Expose @@ -28,11 +27,20 @@ public class AmbassadorConfig { @Expose private boolean debugMode = false; - private AmbassadorConfig(boolean silenceWarnings, boolean bypassRegistryCheck, boolean bypassModCheck, boolean debugMode) { + @Expose + private boolean enableKickReset = false; + + @Expose + private String kickReconnectMessageString = "Please reconnect."; + + private AmbassadorConfig(boolean silenceWarnings, boolean bypassRegistryCheck, boolean bypassModCheck, + boolean debugMode, boolean enableKickReset, String kickReconnectMessageString) { this.silenceWarnings = silenceWarnings; this.bypassRegistryCheck = bypassRegistryCheck; this.bypassModCheck = bypassModCheck; this.debugMode = debugMode; + this.enableKickReset = enableKickReset; + this.kickReconnectMessageString = kickReconnectMessageString; }; public static AmbassadorConfig read(Path path) throws IOException { @@ -59,6 +67,9 @@ public class AmbassadorConfig { boolean silenceWarnings = config.getOrElse("silence-warnings", false); + String kickReconnectMessageString = config.getOrElse("disconnect-reset-message", + "Please reconnect."); + //Upgrade config if (configVersion <= 1.2) { Files.delete(path); @@ -70,6 +81,7 @@ public class AmbassadorConfig { .build(); config.load(); config.set("silence-warnings", silenceWarnings); + config.set("reconnect-message", kickReconnectMessageString); } int serverSwitchCancellationTime = config.getOrElse("serverRedirectTimeout", 30); @@ -80,7 +92,13 @@ public class AmbassadorConfig { boolean debugMode = config.getOrElse("debug-mode", false); - return new AmbassadorConfig(bypassRegistryCheck, bypassModCheck, silenceWarnings, debugMode); + boolean enableKickReset = config.getOrElse("enable-kick-reset", false); + + kickReconnectMessageString = config.getOrElse("reconnect-message", + "Please reconnect."); + + return new AmbassadorConfig(bypassRegistryCheck, bypassModCheck, silenceWarnings, + debugMode, enableKickReset, kickReconnectMessageString); } public int getServerSwitchCancellationTime() { @@ -102,4 +120,12 @@ public class AmbassadorConfig { public boolean isDebugMode() { return debugMode; } + + public boolean isEnableKickReset() { + return enableKickReset; + } + + public String getKickReconnectMessageString() { + return kickReconnectMessageString; + } } diff --git a/src/main/java/org/adde0109/ambassador/forge/VelocityForgeBackendConnectionPhase.java b/src/main/java/org/adde0109/ambassador/forge/VelocityForgeBackendConnectionPhase.java index d473b96..0c3effe 100644 --- a/src/main/java/org/adde0109/ambassador/forge/VelocityForgeBackendConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/forge/VelocityForgeBackendConnectionPhase.java @@ -72,6 +72,10 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas VelocityForgeClientConnectionPhase clientPhase = (VelocityForgeClientConnectionPhase) player.getPhase(); + if (!player.isActive()) { + return; + } + if (!clientPhase.consideredComplete()) { //Initial Forge if (message instanceof ModListPacket modListPacket) { @@ -92,10 +96,16 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas if (clientPhase.forgeHandshake.getModListReplyPacket() == null) { //We have nothing to respond with during this handshake. Unable to proceed. - Ambassador.getInstance().logger.error("Unable for {} to switch servers. " + - "Vanilla({}) -> Forge({}) switch without reset is not yet supported!", player.getGameProfile().getName(), - player.getConnectedServer().getServerInfo().getName(), server.getServerInfo().getName()); - server.disconnect(); + if (Ambassador.getInstance().config.isEnableKickReset()) { + //Kick-reset + Ambassador.getInstance().reconnectSwitchPlayer(player); + } else { + Ambassador.getInstance().logger.error("Unable for {} to switch servers. Vanilla({}) -> Forge({}) switch " + + "without client side mod or kick-reset enabled is not yet supported!", + player.getGameProfile().getName(), player.getConnectedServer().getServerInfo().getName(), + server.getServerInfo().getName()); + server.disconnect(); + } return; } @@ -124,6 +134,9 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas if (Ambassador.getInstance().config.isBypassRegistryCheck() || clientPhase.forgeHandshake.isCompatible(handshake)) { server.ensureConnected().write(clientPhase.forgeHandshake.getModListReplyPacket()); + } else if (Ambassador.getInstance().config.isEnableKickReset()) { + //Kick-reset + Ambassador.getInstance().reconnectSwitchPlayer(player); } else { Ambassador.getInstance().logger.error("Unable to switch due to the registries of " + server.getServer().getServerInfo().getName() + " being different from the registries of " + diff --git a/src/main/java/org/adde0109/ambassador/forge/VelocityForgeClientConnectionPhase.java b/src/main/java/org/adde0109/ambassador/forge/VelocityForgeClientConnectionPhase.java index 3f4da0a..6b065c8 100644 --- a/src/main/java/org/adde0109/ambassador/forge/VelocityForgeClientConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/forge/VelocityForgeClientConnectionPhase.java @@ -25,6 +25,7 @@ import org.adde0109.ambassador.velocity.client.OutboundSuccessHolder; import org.adde0109.ambassador.velocity.client.ClientPacketQueue; import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase { @@ -255,6 +256,8 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase buf.writeBytes((player.getVirtualHost().get().getHostName() + ":" + player.getVirtualHost().get().getPort()).getBytes(StandardCharsets.UTF_8)); player.getConnection().write(new PluginMessagePacket("srvredirect:red", buf)); + + Ambassador.getInstance().reconnectSwitchPlayer(player); } }; diff --git a/src/main/resources/default-ambassador.toml b/src/main/resources/default-ambassador.toml index a0e3922..aaf4fe1 100644 --- a/src/main/resources/default-ambassador.toml +++ b/src/main/resources/default-ambassador.toml @@ -1,5 +1,5 @@ # Do not change this -config-version = "2.0" +config-version = "2.1" # How much time the player has to reconnect before canceling the server switch. (In seconds) # Only for players with ServerRedirect mod installed. Set to -1 to disable ServerRedirect mod support. @@ -17,5 +17,13 @@ bypass-registry-checks = false # Warning: This is a safety measure for when bypassRegistryCheck is true. Setting this to also true can cause crashes. bypass-mod-checks = false -#Only for debug/troubleshooting -debug-mode = false \ No newline at end of file +# Only for debug/troubleshooting +debug-mode = false + +# Make the player reconnect when server switching as a last resort if +# the player can't reset or switch servers without resetting. +enable-kick-reset = false + +# Message to display when player gets kicked due to server switching according to enable-kick-reset. +# This input is deserialzied as MiniMessage and support formating according to the MiniMessage format. +reconnect-message = "Please reconnect."