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."