Compare commits

...

5 Commits

Author SHA1 Message Date
Adrian Bergqvist
fbf12f90e7
Update README.md 2024-06-07 00:24:44 +02:00
Adrian Bergqvist
b2a9eeffa1
1 small extra fix 2024-02-28 20:40:50 +01:00
Icosider
eaa974593c
Fix loading config
Refactoring AmbassadorConfig
2024-02-27 15:30:42 +03:00
Adrian Bergqvist
0fcf6af72f
Fixed config 2024-02-17 18:40:28 +01:00
Adrian Bergqvist
c7c2e1cd33
Implemented kick-reset again with config option 2024-02-17 18:11:04 +01:00
7 changed files with 146 additions and 81 deletions

View File

@ -3,7 +3,8 @@
This is a Velocity plugin that makes it possible to host a modern Forge server behind a Velocity proxy! This is a Velocity plugin that makes it possible to host a modern Forge server behind a Velocity proxy!
Unlike other solutions, this plugin does not require any special modifications to the backend server nor the client. (The player doesn't need to do anything) Unlike other solutions, this plugin does not require any special modifications to the backend server nor the client. (The player doesn't need to do anything)
## Only for 1.13-1.20.1
Velocity has now added built-in support for newer mc versions and therefore don't need Ambassador. You might still need PCF mod on the server-side, for more info please visit: https://github.com/adde0109/Proxy-Compatible-Forge
## How to get started: ## How to get started:
1. Download and install this plugin to your proxy. 1. Download and install this plugin to your proxy.
2. After starting the server, configure the plugin it to your liking using the config file found in the folder "Ambassador". 2. After starting the server, configure the plugin it to your liking using the config file found in the folder "Ambassador".

View File

@ -5,7 +5,7 @@ plugins {
} }
group = "org.adde0109" group = "org.adde0109"
version = "1.5.2-beta" version = "1.5.3-beta"
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -19,6 +19,7 @@ import java.util.Map;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.network.ConnectionManager; import com.velocitypowered.proxy.network.ConnectionManager;
import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.DisconnectPacket; 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.buffer.ByteBuf;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer; 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.VelocityBackendChannelInitializer;
import org.adde0109.ambassador.velocity.VelocityServerChannelInitializer; import org.adde0109.ambassador.velocity.VelocityServerChannelInitializer;
import org.adde0109.ambassador.velocity.VelocityEventHandler; 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.api.network.ProtocolVersion.MINECRAFT_1_19;
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.mapSet; 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 { public class Ambassador {
//Don't forget to update checkCompatibleVersion() when changing this value //Don't forget to update checkCompatibleVersion() when changing this value
@ -153,6 +156,16 @@ public class Ambassador {
return TEMPORARY_FORCED; 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() { private void initMetrics() {
Metrics metrics = metricsFactory.make(this, 15655); Metrics metrics = metricsFactory.make(this, 15655);
} }

View File

@ -14,92 +14,119 @@ import java.nio.file.Path;
public class AmbassadorConfig { public class AmbassadorConfig {
@Expose @Expose
private int serverSwitchCancellationTime = 30; private int serverSwitchCancellationTime = 30;
@Expose @Expose
private boolean silenceWarnings = false; private boolean silenceWarnings = false;
@Expose
private boolean bypassRegistryCheck = false;
@Expose
private boolean bypassModCheck = false;
@Expose @Expose
private boolean bypassRegistryCheck = false; private boolean debugMode = false;
@Expose
private boolean bypassModCheck = false;
@Expose @Expose
private boolean debugMode = false; private boolean enableKickReset = false;
private AmbassadorConfig(boolean silenceWarnings, boolean bypassRegistryCheck, boolean bypassModCheck, boolean debugMode) { @Expose
this.silenceWarnings = silenceWarnings; private String kickReconnectMessageString = "<red>Please reconnect.</red>";
this.bypassRegistryCheck = bypassRegistryCheck;
this.bypassModCheck = bypassModCheck;
this.debugMode = debugMode;
};
public static AmbassadorConfig read(Path path) throws IOException { private AmbassadorConfig(boolean silenceWarnings, boolean bypassRegistryCheck, boolean bypassModCheck,
URL defaultConfigLocation = AmbassadorConfig.class.getClassLoader() boolean debugMode, boolean enableKickReset, String kickReconnectMessageString) {
.getResource("default-ambassador.toml"); this.silenceWarnings = silenceWarnings;
if (defaultConfigLocation == null) { this.bypassRegistryCheck = bypassRegistryCheck;
throw new RuntimeException("Default configuration file does not exist."); this.bypassModCheck = bypassModCheck;
this.debugMode = debugMode;
this.enableKickReset = enableKickReset;
this.kickReconnectMessageString = kickReconnectMessageString;
} }
CommentedFileConfig config = CommentedFileConfig.builder(path) public static AmbassadorConfig read(Path path) throws IOException {
.defaultData(defaultConfigLocation) URL defaultConfigLocation = AmbassadorConfig.class.getClassLoader()
.autosave() .getResource("default-ambassador.toml");
.preserveInsertionOrder() if (defaultConfigLocation == null) {
.sync() throw new RuntimeException("Default configuration file does not exist.");
.build(); }
config.load();
double configVersion; CommentedFileConfig config = CommentedFileConfig.builder(path)
try { .defaultData(defaultConfigLocation)
configVersion = Double.parseDouble(config.getOrElse("config-version", "1.0")); .autosave()
} catch (NumberFormatException e) { .preserveInsertionOrder()
configVersion = 1.0; .sync()
.build();
config.load();
double configVersion;
try {
configVersion = Double.parseDouble(config.getOrElse("config-version", "1.0"));
} catch (NumberFormatException e) {
configVersion = 1.0;
}
boolean silenceWarnings = config.getOrElse("silence-warnings", false);
int serverSwitchCancellationTime = config.getOrElse("serverRedirectTimeout", 30);
boolean bypassRegistryCheck = config.getOrElse("bypass-registry-checks", false);
boolean bypassModCheck = config.getOrElse("bypass-mod-checks", false);
boolean debugMode = config.getOrElse("debug-mode", false);
String kickReconnectMessageString = config.getOrElse("disconnect-reset-message",
config.getOrElse("reconnect-message", "<red>Please reconnect.</red>"));
//Upgrade config
if (configVersion <= 2.0) {
Files.delete(path);
config = CommentedFileConfig.builder(path)
.defaultData(defaultConfigLocation)
.autosave()
.preserveInsertionOrder()
.sync()
.build();
config.load();
config.set("silence-warnings", silenceWarnings);
config.set("serverRedirectTimeout", serverSwitchCancellationTime);
config.set("bypass-registry-checks", bypassRegistryCheck);
config.set("bypass-mod-checks", bypassModCheck);
config.set("debug-mode", debugMode);
config.set("reconnect-message", kickReconnectMessageString);
}
boolean enableKickReset = config.getOrElse("enable-kick-reset", false);
return new AmbassadorConfig(silenceWarnings, bypassRegistryCheck, bypassModCheck,
debugMode, enableKickReset, kickReconnectMessageString);
} }
boolean silenceWarnings = config.getOrElse("silence-warnings", false); public int getServerSwitchCancellationTime() {
return serverSwitchCancellationTime;
//Upgrade config
if (configVersion <= 1.2) {
Files.delete(path);
config = CommentedFileConfig.builder(path)
.defaultData(defaultConfigLocation)
.autosave()
.preserveInsertionOrder()
.sync()
.build();
config.load();
config.set("silence-warnings", silenceWarnings);
} }
int serverSwitchCancellationTime = config.getOrElse("serverRedirectTimeout", 30); public boolean isSilenceWarnings() {
return silenceWarnings;
}
boolean bypassRegistryCheck = config.getOrElse("bypass-registry-checks", false); public boolean isBypassRegistryCheck() {
return bypassRegistryCheck;
}
boolean bypassModCheck = config.getOrElse("bypass-mod-checks", false); public boolean isBypassModCheck() {
return bypassModCheck;
}
boolean debugMode = config.getOrElse("debug-mode", false); public boolean isDebugMode() {
return debugMode;
}
return new AmbassadorConfig(bypassRegistryCheck, bypassModCheck, silenceWarnings, debugMode); public boolean isEnableKickReset() {
} return enableKickReset;
}
public int getServerSwitchCancellationTime() { public String getKickReconnectMessageString() {
return serverSwitchCancellationTime; return kickReconnectMessageString;
} }
public boolean isSilenceWarnings() {
return silenceWarnings;
}
public boolean isBypassRegistryCheck() {
return bypassRegistryCheck;
}
public boolean isBypassModCheck() {
return bypassModCheck;
}
public boolean isDebugMode() {
return debugMode;
}
} }

View File

@ -72,6 +72,10 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas
VelocityForgeClientConnectionPhase clientPhase = (VelocityForgeClientConnectionPhase) player.getPhase(); VelocityForgeClientConnectionPhase clientPhase = (VelocityForgeClientConnectionPhase) player.getPhase();
if (!player.isActive()) {
return;
}
if (!clientPhase.consideredComplete()) { if (!clientPhase.consideredComplete()) {
//Initial Forge //Initial Forge
if (message instanceof ModListPacket modListPacket) { if (message instanceof ModListPacket modListPacket) {
@ -92,10 +96,16 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas
if (clientPhase.forgeHandshake.getModListReplyPacket() == null) { if (clientPhase.forgeHandshake.getModListReplyPacket() == null) {
//We have nothing to respond with during this handshake. Unable to proceed. //We have nothing to respond with during this handshake. Unable to proceed.
Ambassador.getInstance().logger.error("Unable for {} to switch servers. " + if (Ambassador.getInstance().config.isEnableKickReset()) {
"Vanilla({}) -> Forge({}) switch without reset is not yet supported!", player.getGameProfile().getName(), //Kick-reset
player.getConnectedServer().getServerInfo().getName(), server.getServerInfo().getName()); Ambassador.getInstance().reconnectSwitchPlayer(player);
server.disconnect(); } 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; return;
} }
@ -124,6 +134,9 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas
if (Ambassador.getInstance().config.isBypassRegistryCheck() || if (Ambassador.getInstance().config.isBypassRegistryCheck() ||
clientPhase.forgeHandshake.isCompatible(handshake)) { clientPhase.forgeHandshake.isCompatible(handshake)) {
server.ensureConnected().write(clientPhase.forgeHandshake.getModListReplyPacket()); server.ensureConnected().write(clientPhase.forgeHandshake.getModListReplyPacket());
} else if (Ambassador.getInstance().config.isEnableKickReset()) {
//Kick-reset
Ambassador.getInstance().reconnectSwitchPlayer(player);
} else { } else {
Ambassador.getInstance().logger.error("Unable to switch due to the registries of " + Ambassador.getInstance().logger.error("Unable to switch due to the registries of " +
server.getServer().getServerInfo().getName() + " being different from the registries of " + server.getServer().getServerInfo().getName() + " being different from the registries of " +

View File

@ -25,6 +25,7 @@ import org.adde0109.ambassador.velocity.client.OutboundSuccessHolder;
import org.adde0109.ambassador.velocity.client.ClientPacketQueue; import org.adde0109.ambassador.velocity.client.ClientPacketQueue;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase { public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase {
@ -255,6 +256,8 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
buf.writeBytes((player.getVirtualHost().get().getHostName() + ":" buf.writeBytes((player.getVirtualHost().get().getHostName() + ":"
+ player.getVirtualHost().get().getPort()).getBytes(StandardCharsets.UTF_8)); + player.getVirtualHost().get().getPort()).getBytes(StandardCharsets.UTF_8));
player.getConnection().write(new PluginMessagePacket("srvredirect:red", buf)); player.getConnection().write(new PluginMessagePacket("srvredirect:red", buf));
Ambassador.getInstance().reconnectSwitchPlayer(player);
} }
}; };

View File

@ -1,5 +1,5 @@
# Do not change this # 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) # 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. # 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. # Warning: This is a safety measure for when bypassRegistryCheck is true. Setting this to also true can cause crashes.
bypass-mod-checks = false bypass-mod-checks = false
#Only for debug/troubleshooting # Only for debug/troubleshooting
debug-mode = false 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 = "<red>Please reconnect.</red>"