feat: Improved server connection selection

Now the selection mode of the server to send the player to is configurable

resolves #40
This commit is contained in:
Adrian3d04 2022-08-13 21:03:34 +00:00
parent 327b3394f1
commit 6775b8080e
6 changed files with 144 additions and 41 deletions

View File

@ -3,6 +3,8 @@ package me.adrianed.authmevelocity.common.configuration;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import me.adrianed.authmevelocity.common.enums.SendMode;
import java.util.List;
@ConfigSerializable
@ -13,6 +15,12 @@ public class ProxyConfiguration {
return this.debug;
}
@Comment("")
private int randomAttempts = 5;
public int randomAttempts() {
return this.randomAttempts;
}
@Comment("List of login/registration servers")
private List<String> authServers = List.of("auth1", "auth2");
public List<String> authServers() {
@ -41,6 +49,17 @@ public class ProxyConfiguration {
public boolean ensureFirstServerIsAuthServer() {
return this.ensureAuthServer;
}
@Comment("""
SendMode
TO_FIRST |
TO_EMPTIEST_SERVE |
RANDOM |
""")
private SendMode sendMode = SendMode.RANDOM;
public SendMode sendMode() {
return this.sendMode;
}
}
@ConfigSerializable
@ -59,6 +78,18 @@ public class ProxyConfiguration {
public List<String> teleportServers() {
return this.teleportServers;
}
// TODO: Improve comments
@Comment("""
SendMode
TO_FIRST |
TO_EMPTIEST_SERVE |
RANDOM |
""")
private SendMode sendMode = SendMode.RANDOM;
public SendMode sendMode() {
return this.sendMode;
}
}
@ConfigSerializable
@ -80,4 +111,5 @@ public class ProxyConfiguration {
}
}

View File

@ -0,0 +1,7 @@
package me.adrianed.authmevelocity.common.enums;
public enum SendMode {
TO_FIRST,
TO_EMPTIEST_SERVER,
RANDOM;
}

View File

@ -2,7 +2,6 @@ package me.adrianed.authmevelocity.velocity.listener;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import com.google.common.io.ByteArrayDataOutput;
@ -18,6 +17,7 @@ import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import me.adrianed.authmevelocity.velocity.AuthMeVelocityPlugin;
import me.adrianed.authmevelocity.velocity.utils.AuthmeUtils;
public class ConnectListener {
private final ProxyServer proxy;
@ -44,12 +44,14 @@ public class ConnectListener {
plugin.logDebug("PlayerChooseInitialServerEvent | Player is in auth server");
return;
}
var config = plugin.config().get();
var server = AuthmeUtils.serverToSend(
config.ensureAuthServer().sendMode(), proxy, config.authServers(), config.randomAttempts());
@Nullable RegisteredServer server = getAvailableServer();
// Velocity takes over in case the initial server is not present
event.setInitialServer(server);
event.setInitialServer(server.object());
continuation.resume();
if (server == null) {
if (server.isEmpty()) {
plugin.logDebug("PlayerChooseInitialServerEvent | Null server");
logger.error("Cannot send the player {} to an auth server", event.getPlayer().getUsername());
}
@ -82,13 +84,4 @@ public class ConnectListener {
sv.sendPluginMessage(AuthMeVelocityPlugin.AUTHMEVELOCITY_CHANNEL, buf.toByteArray()));
}
}
// TODO: Implement #40
private @Nullable RegisteredServer getAvailableServer() {
for(String sv : plugin.config().get().authServers()){
Optional<RegisteredServer> opt = proxy.getServer(sv);
if (opt.isPresent()) return opt.get();
}
return null;
}
}

View File

@ -1,7 +1,5 @@
package me.adrianed.authmevelocity.velocity.listener;
import java.util.List;
import java.util.Random;
import java.util.Locale;
import me.adrianed.authmevelocity.api.velocity.event.PreSendOnLoginEvent;
@ -14,8 +12,6 @@ import me.adrianed.authmevelocity.common.MessageType;
import com.google.common.io.ByteArrayDataInput;
import com.velocitypowered.api.event.Continuation;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.ResultedEvent.GenericResult;
import com.velocitypowered.api.proxy.ConnectionRequestBuilder.Result;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
@ -23,6 +19,7 @@ import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import me.adrianed.authmevelocity.velocity.AuthMeVelocityPlugin;
import me.adrianed.authmevelocity.velocity.utils.AuthmeUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -31,20 +28,18 @@ import org.slf4j.Logger;
public class PluginMessageListener {
private final ProxyServer proxy;
private final Logger logger;
private final Random rm;
private final AuthMeVelocityPlugin plugin;
public PluginMessageListener(@NotNull ProxyServer proxy, @NotNull Logger logger, AuthMeVelocityPlugin plugin) {
this.proxy = proxy;
this.logger = logger;
this.rm = new Random();
this.plugin = plugin;
}
@Subscribe
public void onPluginMessage(final PluginMessageEvent event, Continuation continuation) {
final boolean cancelled = !event.getResult().isAllowed()
||!(event.getSource() instanceof ServerConnection)
|| !(event.getSource() instanceof ServerConnection)
|| !event.getIdentifier().equals(AuthMeVelocityPlugin.AUTHMEVELOCITY_CHANNEL);
if (cancelled) {
continuation.resume();
@ -52,7 +47,7 @@ public class PluginMessageListener {
return;
}
final ServerConnection connection = ((ServerConnection)event.getSource());
final ServerConnection connection = (ServerConnection) event.getSource();
event.setResult(PluginMessageEvent.ForwardResult.handled());
@ -66,7 +61,7 @@ public class PluginMessageListener {
switch (type) {
case LOGIN -> {
plugin.logDebug("PluginMessageEvent | Login type");
if (player != null && plugin.addPlayer(player)){
if (player != null && plugin.addPlayer(player)) {
proxy.getEventManager().fireAndForget(new ProxyLoginEvent(player));
this.createServerConnectionRequest(player, connection);
plugin.logDebug("PluginMessageEvent | Player not null");
@ -88,7 +83,7 @@ public class PluginMessageListener {
}
case UNREGISTER -> {
plugin.logDebug("PluginMessageEvent | Unregister type");
if(player != null) {
if (player != null) {
plugin.logDebug("PluginMessageEvent | Player not null");
proxy.getEventManager().fireAndForget(new ProxyUnregisterEvent(player));
}
@ -108,32 +103,35 @@ public class PluginMessageListener {
}
final RegisteredServer loginServer = player.getCurrentServer().orElse(connection).getServer();
final String randomServer = this.getRandomServer();
proxy.getServer(randomServer).ifPresentOrElse(server ->
proxy.getEventManager().fire(new PreSendOnLoginEvent(player, loginServer, server))
.thenApply(PreSendOnLoginEvent::getResult)
.thenApply(GenericResult::isAllowed)
.thenAcceptAsync(allowed -> {
if (!allowed.booleanValue()) {
var config = plugin.config().get();
var toSend = AuthmeUtils.serverToSend(
config.sendOnLogin().sendMode(), proxy, config.authServers(), config.randomAttempts());
if (toSend.isEmpty()) {
if (toSend.string() != null) {
logger.warn("The server {} does not exist", toSend.string());
} else {
logger.warn("There is not valid server to send");
}
return;
}
proxy.getEventManager().fire(new PreSendOnLoginEvent(player, loginServer, toSend.object()))
.thenAccept(event -> {
if (!event.getResult().isAllowed()) {
return;
}
player.createConnectionRequest(server)
player.createConnectionRequest(event.getResult().server())
.connect()
.thenApply(Result::isSuccessful)
.thenAcceptAsync(result -> {
if(!result.booleanValue()) {
if (!result.isSuccessful()) {
logger.info("Unable to connect the player {} to the server {}",
player.getUsername(),
server.getServerInfo().getName());
result.getAttemptedConnection().getServerInfo().getName());
}
});
})
, () -> logger.warn("The server {} does not exist", randomServer));
}
private String getRandomServer() {
final List<String> serverList = plugin.config().get().sendOnLogin().teleportServers();
return serverList.get(rm.nextInt(serverList.size()));
});
}
}

View File

@ -1,9 +1,17 @@
package me.adrianed.authmevelocity.velocity.utils;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import org.jetbrains.annotations.NotNull;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import me.adrianed.authmevelocity.common.enums.SendMode;
public class AuthmeUtils {
//Origin: https://github.com/4drian3d/ChatRegulator/blob/main/src/main/java/me/dreamerzero/chatregulator/utils/CommandUtils.java#L71
/**
@ -18,5 +26,55 @@ public class AuthmeUtils {
}
return string.substring(0, index);
}
private static final Random RANDOM = new Random();
public static Pair<RegisteredServer> serverToSend(SendMode sendMode, ProxyServer proxy, List<String> servers, int attempts) {
return switch(sendMode) {
case TO_FIRST -> {
Optional<RegisteredServer> sv;
for (final String st : servers) {
sv = proxy.getServer(st);
if (sv.isPresent()) yield Pair.of(st, sv.get());
}
yield Pair.of(null, null);
}
case TO_EMPTIEST_SERVER -> {
RegisteredServer emptiest = null;
Optional<RegisteredServer> optional = Optional.empty();
for (final String st : servers) {
optional = proxy.getServer(st);
if (optional.isPresent()) {
RegisteredServer actualsv = optional.get();
int actualConnected = actualsv.getPlayersConnected().size();
if (actualConnected == 0) {
yield Pair.of(st, actualsv);
}
if (emptiest == null || actualConnected < emptiest.getPlayersConnected().size()) {
emptiest = actualsv;
}
}
}
yield Pair.of(optional.map(sv -> sv.getServerInfo().getName()).orElse(null), emptiest);
}
case RANDOM -> {
Optional<RegisteredServer> server;
if (servers.size() == 1) {
server = proxy.getServer(servers.get(0));
if (server.isPresent()) {
yield Pair.of(server.get().getServerInfo().getName(), server.get());
}
}
for (int i = 0; i < attempts; i++) {
int value = RANDOM.nextInt(servers.size());
server = proxy.getServer(servers.get(value));
if (server.isPresent()) {
yield Pair.of(server.get().getServerInfo().getName(), server.get());
}
}
yield Pair.of(null, null);
}
};
}
private AuthmeUtils() {}
}

View File

@ -0,0 +1,15 @@
package me.adrianed.authmevelocity.velocity.utils;
public record Pair<O>(String string, O object) {
public boolean isPresent() {
return this.object != null;
}
public boolean isEmpty() {
return object == null;
}
public static <O> Pair<O> of(String string, O object) {
return new Pair<>(string, object);
}
}