Config file

This commit is contained in:
Adrian Bergqvist 2023-02-09 22:41:29 +01:00
parent c751342ad8
commit e2c49f35e6
No known key found for this signature in database
GPG Key ID: FAE7D8EDE225E686
7 changed files with 148 additions and 20 deletions

View File

@ -19,9 +19,8 @@ dependencies {
compileOnly("com.velocitypowered:velocity-api")
compileOnly("com.velocitypowered:velocity-proxy")
annotationProcessor("com.velocitypowered:velocity-api")
implementation("com.electronwill.night-config:toml:3.6.6")
compileOnly("com.electronwill.night-config:toml:3.6.6")
implementation("org.bstats:bstats-velocity:3.0.0")
implementation("org.apache.commons:commons-collections4:4.4")
compileOnly("io.netty:netty-buffer:4.1.86.Final")
compileOnly("io.netty:netty-transport:4.1.86.Final")
compileOnly("io.netty:netty-codec:4.1.86.Final")

View File

@ -4,6 +4,7 @@ import com.google.inject.Inject;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyReloadEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
@ -11,12 +12,13 @@ import com.velocitypowered.api.proxy.ProxyServer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.network.BackendChannelInitializer;
import com.velocitypowered.proxy.network.ConnectionManager;
import com.velocitypowered.proxy.network.ServerChannelInitializer;
import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier;
import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentPropertyRegistry;
import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentPropertySerializer;
@ -28,11 +30,11 @@ import org.adde0109.ambassador.velocity.VelocityEventHandler;
import org.adde0109.ambassador.velocity.protocol.EnumArgumentProperty;
import org.adde0109.ambassador.velocity.protocol.EnumArgumentPropertySerializer;
import org.adde0109.ambassador.velocity.protocol.ModIdArgumentProperty;
import org.bstats.charts.SingleLineChart;
import org.bstats.velocity.Metrics;
import org.slf4j.Logger;
import java.nio.file.Path;
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;
@ -45,6 +47,10 @@ public class Ambassador {
private final Metrics.Factory metricsFactory;
private final Path dataDirectory;
public AmbassadorConfig config;
private static final MapWithExpiration<String, RegisteredServer> TEMPORARY_FORCED = new MapWithExpiration<>();
private static Ambassador instance;
public static Ambassador getInstance() {
return instance;
@ -61,12 +67,36 @@ public class Ambassador {
}
@Subscribe(order = PostOrder.LAST)
public void onProxyInitialization(ProxyInitializeEvent event) throws ReflectiveOperationException {
public void onProxyInitialization(ProxyInitializeEvent event) {
initMetrics();
server.getEventManager().register(this, new VelocityEventHandler(this));
try {
Files.createDirectories(dataDirectory);
inject();
Path configPath = dataDirectory.resolve("Ambassador.toml");
config = AmbassadorConfig.read(configPath);
config.validate();
inject();
server.getEventManager().register(this, new VelocityEventHandler(this));
} catch (Exception e) {
logger.error(e.toString());
}
}
@Subscribe
public void onProxyReload(ProxyReloadEvent event) {
try {
Path configPath = dataDirectory.resolve("Ambassador.toml");
final AmbassadorConfig newconfig = AmbassadorConfig.read(configPath);
newconfig.validate();
config = newconfig;
} catch (Exception e) {
logger.error(e.toString());
logger.warn("Reload unsuccessful, old config will be used.");
}
}
private void inject() throws ReflectiveOperationException {
@ -97,7 +127,35 @@ public class Ambassador {
}
public static MapWithExpiration<String, RegisteredServer> getTemporaryForced() {
return TEMPORARY_FORCED;
}
private void initMetrics() {
Metrics metrics = metricsFactory.make(this, 15655);
}
public static class MapWithExpiration<K, V> {
private final Map<K, ExpiringValue<V, Long>> expirationMap = new HashMap<>();
public V remove(K key) {
ExpiringValue<V, Long> expiringValue = expirationMap.remove(key);
if (expiringValue != null && expiringValue.value > System.currentTimeMillis()) {
return expiringValue.key;
} else {
return null;
}
}
public void put(K key, V value, int expirationTime, TimeUnit unit) {
expirationMap.values().removeIf((v) -> v.value <= System.currentTimeMillis());
expirationMap.put(key, new ExpiringValue<>(value,System.currentTimeMillis() + unit.toMillis(expirationTime)));
}
private record ExpiringValue<K, V>(K key, V value) {
}
}
}

View File

@ -0,0 +1,67 @@
package org.adde0109.ambassador;
import com.electronwill.nightconfig.core.conversion.InvalidValueException;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.google.gson.annotations.Expose;
import java.net.URL;
import java.nio.file.Path;
public class AmbassadorConfig {
@Expose
private int resetTimeout = 1000;
@Expose
private String disconnectResetMessage = "Please reconnect";
@Expose
private int serverSwitchCancellationTime = 120;
private AmbassadorConfig(int resetTimeout, String kickResetMessage, int serverSwitchCancellationTime) {
this.resetTimeout = resetTimeout;
this.disconnectResetMessage = kickResetMessage;
this.serverSwitchCancellationTime = serverSwitchCancellationTime;
};
public void validate() {
final int connectionTimeout = Ambassador.getInstance().server.getConfiguration().getConnectTimeout();
if (resetTimeout >= connectionTimeout) {
throw new InvalidValueException("'reset-timeout' can't be larger than nor equal to 'connection-timeout': reset-timeout=" + resetTimeout + " connection-timeout=" + connectionTimeout);
}
}
public static AmbassadorConfig read(Path path) {
URL defaultConfigLocation = AmbassadorConfig.class.getClassLoader()
.getResource("default-ambassador.toml");
if (defaultConfigLocation == null) {
throw new RuntimeException("Default configuration file does not exist.");
}
CommentedFileConfig config = CommentedFileConfig.builder(path)
.defaultData(defaultConfigLocation)
.autosave()
.preserveInsertionOrder()
.sync()
.build();
config.load();
int resetTimeout = config.getIntOrElse("reset-timeout", 3000);
String kickResetMessage = config.getOrElse("disconnect-reset-message", "Please reconnect");
int serverSwitchCancellationTime = config.getIntOrElse("server-switch-cancellation-time", 120000);
return new AmbassadorConfig(resetTimeout, kickResetMessage, serverSwitchCancellationTime);
}
public int getResetTimeout() {
return resetTimeout;
}
public String getDisconnectResetMessage() {
return disconnectResetMessage;
}
public int getServerSwitchCancellationTime() {
return serverSwitchCancellationTime;
}
}

View File

@ -7,7 +7,6 @@ import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.network.Connections;
import com.velocitypowered.proxy.protocol.StateRegistry;
@ -51,7 +50,7 @@ public class FML2CRPMClientConnectionPhase extends VelocityForgeClientConnection
ScheduledFuture<?> scheduledFuture = connection.eventLoop().schedule(()-> {
connection.getChannel().pipeline().remove(ForgeConstants.RESET_LISTENER);
future.complete(false);
},5, TimeUnit.SECONDS);
}, Ambassador.getInstance().config.getResetTimeout(), TimeUnit.MILLISECONDS);
connection.getChannel().pipeline().addBefore(Connections.MINECRAFT_DECODER, ForgeConstants.RESET_LISTENER,new FML2CRPMResetCompleteDecoder());
getPayloadManager().listenFor(98).thenAccept(ignore -> {
if (scheduledFuture.cancel(false)) {

View File

@ -9,10 +9,9 @@ import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
import net.kyori.adventure.text.Component;
import org.adde0109.ambassador.Ambassador;
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
import org.apache.commons.collections4.map.PassiveExpiringMap;
import java.lang.reflect.Method;
import java.util.Arrays;
@ -21,10 +20,6 @@ import java.util.concurrent.TimeUnit;
public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhase {
private static String OUTBOUND_CATCHER_NAME = "ambassador-catcher";
private static final PassiveExpiringMap<String,RegisteredServer> TEMPORARY_FORCED = new PassiveExpiringMap<>(120, TimeUnit.SECONDS);
private Throwable throwable;
private RegisteredServer triedServer;
private Continuation continuation;
@ -56,7 +51,7 @@ public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhas
this.continuation = continuation;
final MinecraftConnection connection = player.getConnection();
forced = TEMPORARY_FORCED.remove(player.getUsername());
forced = Ambassador.getTemporaryForced().remove(player.getUsername());
if (forced != null) {
player.createConnectionRequest(forced).fireAndForget();
} else {
@ -75,8 +70,8 @@ public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhas
CompletableFuture<Boolean> future = newPhase.reset(server,player);
future.thenAccept(success -> {
if (!success) {
TEMPORARY_FORCED.put(player.getUsername(),server);
player.disconnect(Component.text("Please reconnect"));
Ambassador.getTemporaryForced().put(player.getUsername(),server, Ambassador.getInstance().config.getServerSwitchCancellationTime(), TimeUnit.SECONDS);
player.disconnect(Component.text(Ambassador.getInstance().config.getDisconnectResetMessage()));
}
});
return future;

View File

@ -29,6 +29,7 @@ public class ForgeFMLConnectionType implements ConnectionType {
@Override
public GameProfile addGameProfileTokensIfRequired(GameProfile original, PlayerInfoForwarding forwardingType) {
//This is meant for Arc light to parse
if (forwardingType == PlayerInfoForwarding.LEGACY) {
return original.addProperties(Collections.singleton(new GameProfile.Property("extraData", "\1FML" + netVersion + "\1", "")));
} else {

View File

@ -0,0 +1,9 @@
# Do not change this
config-version = "1.0"
# How long to wait for the client to reset before disconnecting (In milliseconds)
reset-timeout = 1000
# Message displayed to the player when disconnected from proxy during server switch.
disconnect-reset-message = "Please reconnect"
# How long the player has to reconnect before canceling the server switch. (In seconds)
server-switch-cancellation-time = 120