3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-12-23 23:00:35 +01:00

Introduce RegisteredServer API

This interface roughly maps the BungeeCord ServerInfo API. Accordingly,
this is a breaking API change, as many of the server-related events and
methods working with server info instances now provide/expect the
RegisteredServer interface instead.
Dieser Commit ist enthalten in:
Andrew Steinborn 2018-09-16 02:26:29 -04:00
Ursprung 88b7407aaf
Commit e1b2dc0d43
19 geänderte Dateien mit 331 neuen und 159 gelöschten Zeilen

Datei anzeigen

@ -3,6 +3,7 @@ package com.velocitypowered.api.event.player;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
@ -13,12 +14,12 @@ import org.checkerframework.checker.nullness.qual.NonNull;
*/
public class KickedFromServerEvent implements ResultedEvent<KickedFromServerEvent.ServerKickResult> {
private final Player player;
private final ServerInfo server;
private final RegisteredServer server;
private final Component originalReason;
private final boolean duringLogin;
private ServerKickResult result;
public KickedFromServerEvent(Player player, ServerInfo server, Component originalReason, boolean duringLogin, Component fancyReason) {
public KickedFromServerEvent(Player player, RegisteredServer server, Component originalReason, boolean duringLogin, Component fancyReason) {
this.player = Preconditions.checkNotNull(player, "player");
this.server = Preconditions.checkNotNull(server, "server");
this.originalReason = Preconditions.checkNotNull(originalReason, "originalReason");
@ -40,7 +41,7 @@ public class KickedFromServerEvent implements ResultedEvent<KickedFromServerEven
return player;
}
public ServerInfo getServer() {
public RegisteredServer getServer() {
return server;
}
@ -91,9 +92,9 @@ public class KickedFromServerEvent implements ResultedEvent<KickedFromServerEven
* when this result is used.
*/
public static class RedirectPlayer implements ServerKickResult {
private final ServerInfo server;
private final RegisteredServer server;
private RedirectPlayer(ServerInfo server) {
private RedirectPlayer(RegisteredServer server) {
this.server = Preconditions.checkNotNull(server, "server");
}
@ -102,7 +103,7 @@ public class KickedFromServerEvent implements ResultedEvent<KickedFromServerEven
return false;
}
public ServerInfo getServer() {
public RegisteredServer getServer() {
return server;
}
@ -111,7 +112,7 @@ public class KickedFromServerEvent implements ResultedEvent<KickedFromServerEven
* @param server the server to send the player to
* @return the redirect result
*/
public static RedirectPlayer create(ServerInfo server) {
public static RedirectPlayer create(RegisteredServer server) {
return new RedirectPlayer(server);
}
}

Datei anzeigen

@ -2,7 +2,7 @@ package com.velocitypowered.api.event.player;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.proxy.server.RegisteredServer;
/**
* This event is fired once the player has successfully connected to the target server and the connection to the previous
@ -10,9 +10,9 @@ import com.velocitypowered.api.proxy.server.ServerInfo;
*/
public class ServerConnectedEvent {
private final Player player;
private final ServerInfo server;
private final RegisteredServer server;
public ServerConnectedEvent(Player player, ServerInfo server) {
public ServerConnectedEvent(Player player, RegisteredServer server) {
this.player = Preconditions.checkNotNull(player, "player");
this.server = Preconditions.checkNotNull(server, "server");
}
@ -21,7 +21,7 @@ public class ServerConnectedEvent {
return player;
}
public ServerInfo getServer() {
public RegisteredServer getServer() {
return server;
}

Datei anzeigen

@ -3,6 +3,7 @@ package com.velocitypowered.api.event.player;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -14,10 +15,10 @@ import java.util.Optional;
*/
public class ServerPreConnectEvent implements ResultedEvent<ServerPreConnectEvent.ServerResult> {
private final Player player;
private final ServerInfo originalServer;
private final RegisteredServer originalServer;
private ServerResult result;
public ServerPreConnectEvent(Player player, ServerInfo originalServer) {
public ServerPreConnectEvent(Player player, RegisteredServer originalServer) {
this.player = Preconditions.checkNotNull(player, "player");
this.originalServer = Preconditions.checkNotNull(originalServer, "originalServer");
this.result = ServerResult.allowed(originalServer);
@ -37,7 +38,7 @@ public class ServerPreConnectEvent implements ResultedEvent<ServerPreConnectEven
this.result = Preconditions.checkNotNull(result, "result");
}
public ServerInfo getOriginalServer() {
public RegisteredServer getOriginalServer() {
return originalServer;
}
@ -57,9 +58,9 @@ public class ServerPreConnectEvent implements ResultedEvent<ServerPreConnectEven
private static final ServerResult DENIED = new ServerResult(false, null);
private final boolean allowed;
private final ServerInfo server;
private final RegisteredServer server;
private ServerResult(boolean allowed, @Nullable ServerInfo server) {
private ServerResult(boolean allowed, @Nullable RegisteredServer server) {
this.allowed = allowed;
this.server = server;
}
@ -69,7 +70,7 @@ public class ServerPreConnectEvent implements ResultedEvent<ServerPreConnectEven
return allowed;
}
public Optional<ServerInfo> getServer() {
public Optional<RegisteredServer> getServer() {
return Optional.ofNullable(server);
}
@ -78,14 +79,14 @@ public class ServerPreConnectEvent implements ResultedEvent<ServerPreConnectEven
if (!allowed) {
return "denied";
}
return "allowed: connect to " + server.getName();
return "allowed: connect to " + server.getServerInfo().getName();
}
public static ServerResult denied() {
return DENIED;
}
public static ServerResult allowed(ServerInfo server) {
public static ServerResult allowed(RegisteredServer server) {
Preconditions.checkNotNull(server, "server");
return new ServerResult(true, server);
}

Datei anzeigen

@ -1,5 +1,6 @@
package com.velocitypowered.api.proxy;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
@ -9,21 +10,21 @@ import java.util.concurrent.CompletableFuture;
/**
* Provides a fluent interface to compose and send a connection request to another server behind the proxy. A connection
* request is created using {@link Player#createConnectionRequest(ServerInfo)}.
* request is created using {@link Player#createConnectionRequest(RegisteredServer)}.
*/
public interface ConnectionRequestBuilder {
/**
* Returns the server that this connection request represents.
* @return the server this request will connect to
*/
@NonNull ServerInfo getServer();
RegisteredServer getServer();
/**
* Initiates the connection to the remote server and emits a result on the {@link CompletableFuture} after the user
* has logged on. No messages will be communicated to the client: the user is responsible for all error handling.
* @return a {@link CompletableFuture} representing the status of this connection
*/
@NonNull CompletableFuture<Result> connect();
CompletableFuture<Result> connect();
/**
* Initiates the connection to the remote server without waiting for a result. Velocity will use generic error

Datei anzeigen

@ -4,6 +4,7 @@ import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.player.PlayerSettings;
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.util.MessagePosition;
import net.kyori.text.Component;
@ -59,10 +60,10 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage
/**
* Creates a new connection request so that the player can connect to another server.
* @param info the server to connect to
* @param server the server to connect to
* @return a new connection request
*/
ConnectionRequestBuilder createConnectionRequest(@NonNull ServerInfo info);
ConnectionRequestBuilder createConnectionRequest(@NonNull RegisteredServer server);
/**
* Sets the tab list header and footer for the player.

Datei anzeigen

@ -5,6 +5,7 @@ import com.velocitypowered.api.command.CommandManager;
import com.velocitypowered.api.event.EventManager;
import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.api.proxy.messages.ChannelRegistrar;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.scheduler.Scheduler;
import com.velocitypowered.api.proxy.server.ServerInfo;
@ -45,23 +46,24 @@ public interface ProxyServer {
int getPlayerCount();
/**
* Retrieves a registered {@link ServerInfo} instance by its name. The search is case-insensitive.
* Retrieves a registered {@link RegisteredServer} instance by its name. The search is case-insensitive.
* @param name the name of the server
* @return the registered server, which may be empty
*/
Optional<ServerInfo> getServerInfo(String name);
Optional<RegisteredServer> getServerInfo(String name);
/**
* Retrieves all {@link ServerInfo}s registered with this proxy.
* Retrieves all {@link RegisteredServer}s registered with this proxy.
* @return the servers registered with this proxy
*/
Collection<ServerInfo> getAllServers();
Collection<RegisteredServer> getAllServers();
/**
* Registers a server with this proxy. A server with this name should not already exist.
* @param server the server to register
* @return the newly registered server
*/
void registerServer(ServerInfo server);
RegisteredServer registerServer(ServerInfo server);
/**
* Unregisters this server from the proxy.

Datei anzeigen

@ -2,6 +2,7 @@ package com.velocitypowered.api.proxy;
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
/**
@ -12,6 +13,12 @@ public interface ServerConnection extends ChannelMessageSource, ChannelMessageSi
* Returns the server that this connection is connected to.
* @return the server this connection is connected to
*/
RegisteredServer getServer();
/**
* Returns the server info for this connection.
* @return the server info for this connection
*/
ServerInfo getServerInfo();
/**

Datei anzeigen

@ -8,6 +8,7 @@ public interface ChannelMessageSink {
* Sends a plugin message to this target.
* @param identifier the channel identifier to send the message on
* @param data the data to send
* @return whether or not the message could be sent
*/
void sendPluginMessage(ChannelIdentifier identifier, byte[] data);
boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data);
}

Datei anzeigen

@ -0,0 +1,30 @@
package com.velocitypowered.api.proxy.server;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
/**
* Represents a server that has been registered with the proxy.
*/
public interface RegisteredServer extends ChannelMessageSink {
/**
* Returns the {@link ServerInfo} for this server.
* @return the server info
*/
ServerInfo getServerInfo();
/**
* Returns a list of all the players currently connected to this server on this proxy.
* @return the players on this proxy
*/
Collection<Player> getPlayersConnected();
/**
* Attempts to ping the remote server and return the server list ping result.
* @return the server ping result from the server
*/
CompletableFuture<ServerPing> ping();
}

Datei anzeigen

@ -10,6 +10,7 @@ import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.Favicon;
import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.api.proxy.server.ServerInfo;
@ -30,7 +31,7 @@ import com.velocitypowered.proxy.scheduler.VelocityScheduler;
import com.velocitypowered.proxy.util.AddressUtil;
import com.velocitypowered.proxy.util.EncryptionUtils;
import com.velocitypowered.proxy.util.Ratelimiter;
import com.velocitypowered.proxy.util.ServerMap;
import com.velocitypowered.proxy.server.ServerMap;
import io.netty.bootstrap.Bootstrap;
import net.kyori.text.Component;
import net.kyori.text.TextComponent;
@ -61,7 +62,7 @@ public class VelocityServer implements ProxyServer {
private VelocityConfiguration configuration;
private NettyHttpClient httpClient;
private KeyPair serverKeyPair;
private final ServerMap servers = new ServerMap();
private final ServerMap servers = new ServerMap(this);
private final VelocityCommandManager commandManager = new VelocityCommandManager();
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
private boolean shutdown = false;
@ -263,19 +264,19 @@ public class VelocityServer implements ProxyServer {
}
@Override
public Optional<ServerInfo> getServerInfo(String name) {
public Optional<RegisteredServer> getServerInfo(String name) {
Preconditions.checkNotNull(name, "name");
return servers.getServer(name);
}
@Override
public Collection<ServerInfo> getAllServers() {
public Collection<RegisteredServer> getAllServers() {
return servers.getAllServers();
}
@Override
public void registerServer(ServerInfo server) {
servers.register(server);
public RegisteredServer registerServer(ServerInfo server) {
return servers.register(server);
}
@Override

Datei anzeigen

@ -6,6 +6,7 @@ import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import net.kyori.text.TextComponent;
import net.kyori.text.event.ClickEvent;
@ -34,7 +35,7 @@ public class ServerCommand implements Command {
if (args.length == 1) {
// Trying to connect to a server.
String serverName = args[0];
Optional<ServerInfo> toConnect = server.getServerInfo(serverName);
Optional<RegisteredServer> toConnect = server.getServerInfo(serverName);
if (!toConnect.isPresent()) {
player.sendMessage(TextComponent.of("Server " + serverName + " doesn't exist.", TextColor.RED));
return;
@ -48,17 +49,19 @@ public class ServerCommand implements Command {
// Assemble the list of servers as components
TextComponent.Builder serverListBuilder = TextComponent.builder("Available servers: ").color(TextColor.YELLOW);
List<ServerInfo> infos = ImmutableList.copyOf(server.getAllServers());
List<RegisteredServer> infos = ImmutableList.copyOf(server.getAllServers());
for (int i = 0; i < infos.size(); i++) {
ServerInfo serverInfo = infos.get(i);
TextComponent infoComponent = TextComponent.of(serverInfo.getName());
if (serverInfo.getName().equals(currentServer)) {
RegisteredServer rs = infos.get(i);
TextComponent infoComponent = TextComponent.of(rs.getServerInfo().getName());
String playersText = rs.getPlayersConnected().size() + " player(s) online";
if (rs.getServerInfo().getName().equals(currentServer)) {
infoComponent = infoComponent.color(TextColor.GREEN)
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Currently connected to this server")));
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
TextComponent.of("Currently connected to this server\n" + playersText)));
} else {
infoComponent = infoComponent.color(TextColor.GRAY)
.clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/server " + serverInfo.getName()))
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to connect to this server")));
.clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/server " + rs.getServerInfo().getName()))
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to connect to this server\n" + playersText)));
}
serverListBuilder.append(infoComponent);
if (i != infos.size() - 1) {
@ -74,11 +77,11 @@ public class ServerCommand implements Command {
public List<String> suggest(CommandSource source, String[] currentArgs) {
if (currentArgs.length == 0) {
return server.getAllServers().stream()
.map(ServerInfo::getName)
.map(rs -> rs.getServerInfo().getName())
.collect(Collectors.toList());
} else if (currentArgs.length == 1) {
return server.getAllServers().stream()
.map(ServerInfo::getName)
.map(rs -> rs.getServerInfo().getName())
.filter(name -> name.regionMatches(true, 0, currentArgs[0], 0, currentArgs[0].length()))
.collect(Collectors.toList());
} else {

Datei anzeigen

@ -25,7 +25,8 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
@Override
public void activated() {
server.getEventManager().fireAndForget(new ServerConnectedEvent(connection.getPlayer(), connection.getServerInfo()));
server.getEventManager().fireAndForget(new ServerConnectedEvent(connection.getPlayer(), connection.getServer()));
connection.getServer().addPlayer(connection.getPlayer());
}
@Override
@ -46,7 +47,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
} else if (packet instanceof Disconnect) {
Disconnect original = (Disconnect) packet;
connection.disconnect();
connection.getPlayer().handleConnectionException(connection.getServerInfo(), original);
connection.getPlayer().handleConnectionException(connection.getServer(), original);
} else if (packet instanceof JoinGame) {
playerHandler.handleBackendJoinGame((JoinGame) packet);
} else if (packet instanceof BossBar) {
@ -112,7 +113,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
@Override
public void exception(Throwable throwable) {
connection.getPlayer().handleConnectionException(connection.getServerInfo(), throwable);
connection.getPlayer().handleConnectionException(connection.getServer(), throwable);
}
public VelocityServer getServer() {
@ -121,10 +122,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
@Override
public void disconnected() {
if (connection.isGracefulDisconnect()) {
return;
connection.getServer().removePlayer(connection.getPlayer());
if (!connection.isGracefulDisconnect()) {
connection.getPlayer().handleConnectionException(connection.getServer(), Disconnect.create(
ConnectionMessages.UNEXPECTED_DISCONNECT));
}
connection.getPlayer().handleConnectionException(connection.getServerInfo(), Disconnect.create(ConnectionMessages.UNEXPECTED_DISCONNECT));
}
private boolean canForwardPluginMessage(PluginMessage message) {

Datei anzeigen

@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.ConnectionRequestBuilder;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
@ -19,6 +20,7 @@ import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.server.VelocityRegisteredServer;
import io.netty.channel.*;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.util.AttributeKey;
@ -38,7 +40,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
static final AttributeKey<CompletableFuture<ConnectionRequestBuilder.Result>> CONNECTION_NOTIFIER =
AttributeKey.newInstance("connection-notification-result");
private final ServerInfo serverInfo;
private final VelocityRegisteredServer registeredServer;
private final ConnectedPlayer proxyPlayer;
private final VelocityServer server;
private MinecraftConnection minecraftConnection;
@ -46,8 +48,8 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
private boolean hasCompletedJoin = false;
private boolean gracefulDisconnect = false;
public VelocityServerConnection(ServerInfo target, ConnectedPlayer proxyPlayer, VelocityServer server) {
this.serverInfo = target;
public VelocityServerConnection(VelocityRegisteredServer registeredServer, ConnectedPlayer proxyPlayer, VelocityServer server) {
this.registeredServer = registeredServer;
this.proxyPlayer = proxyPlayer;
this.server = server;
}
@ -72,7 +74,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
ch.pipeline().addLast(HANDLER, connection);
}
})
.connect(serverInfo.getAddress())
.connect(registeredServer.getServerInfo().getAddress())
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
@ -94,7 +96,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
// BungeeCord IP forwarding is simply a special injection after the "address" in the handshake,
// separated by \0 (the null byte). In order, you send the original host, the player's IP, their
// UUID (undashed), and if you are in online-mode, their login properties (retrieved from Mojang).
return serverInfo.getAddress().getHostString() + "\0" +
return registeredServer.getServerInfo().getAddress().getHostString() + "\0" +
proxyPlayer.getRemoteAddress().getHostString() + "\0" +
proxyPlayer.getProfile().getId() + "\0" +
GSON.toJson(proxyPlayer.getProfile().getProperties());
@ -112,9 +114,9 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
} else if (proxyPlayer.getConnection().isLegacyForge()) {
handshake.setServerAddress(handshake.getServerAddress() + "\0FML\0");
} else {
handshake.setServerAddress(serverInfo.getAddress().getHostString());
handshake.setServerAddress(registeredServer.getServerInfo().getAddress().getHostString());
}
handshake.setPort(serverInfo.getAddress().getPort());
handshake.setPort(registeredServer.getServerInfo().getAddress().getPort());
minecraftConnection.write(handshake);
int protocolVersion = proxyPlayer.getConnection().getProtocolVersion();
@ -136,8 +138,14 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
return minecraftConnection;
}
@Override
public VelocityRegisteredServer getServer() {
return registeredServer;
}
@Override
public ServerInfo getServerInfo() {
return serverInfo;
return registeredServer.getServerInfo();
}
@Override
@ -155,17 +163,18 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
@Override
public String toString() {
return "[server connection] " + proxyPlayer.getProfile().getName() + " -> " + serverInfo.getName();
return "[server connection] " + proxyPlayer.getProfile().getName() + " -> " + registeredServer.getServerInfo().getName();
}
@Override
public void sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
Preconditions.checkNotNull(identifier, "identifier");
Preconditions.checkNotNull(data, "data");
PluginMessage message = new PluginMessage();
message.setChannel(identifier.getId());
message.setData(data);
minecraftConnection.write(message);
return true;
}
public boolean isLegacyForge() {

Datei anzeigen

@ -11,6 +11,7 @@ import com.velocitypowered.api.proxy.player.PlayerSettings;
import com.velocitypowered.api.proxy.ConnectionRequestBuilder;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.MessagePosition;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.proxy.VelocityServer;
@ -24,6 +25,7 @@ import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.protocol.packet.ClientSettings;
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
import com.velocitypowered.proxy.server.VelocityRegisteredServer;
import com.velocitypowered.proxy.util.ThrowableUtils;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.proxy.protocol.packet.Disconnect;
@ -159,8 +161,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public ConnectionRequestBuilder createConnectionRequest(@NonNull ServerInfo info) {
return new ConnectionRequestBuilderImpl(info);
public ConnectionRequestBuilder createConnectionRequest(@NonNull RegisteredServer server) {
return new ConnectionRequestBuilderImpl(server);
}
@Override
@ -184,57 +186,57 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
return connectedServer;
}
public void handleConnectionException(ServerInfo info, Throwable throwable) {
public void handleConnectionException(RegisteredServer server, Throwable throwable) {
String error = ThrowableUtils.briefDescription(throwable);
String userMessage;
if (connectedServer != null && connectedServer.getServerInfo().equals(info)) {
userMessage = "Exception in server " + info.getName();
if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) {
userMessage = "Exception in server " + server.getServerInfo().getName();
} else {
logger.error("{}: unable to connect to server {}", this, info.getName(), throwable);
userMessage = "Exception connecting to server " + info.getName();
logger.error("{}: unable to connect to server {}", this, server.getServerInfo().getName(), throwable);
userMessage = "Exception connecting to server " + server.getServerInfo().getName();
}
handleConnectionException(info, null, TextComponent.builder()
handleConnectionException(server, null, TextComponent.builder()
.content(userMessage + ": ")
.color(TextColor.RED)
.append(TextComponent.of(error, TextColor.WHITE))
.build());
}
public void handleConnectionException(ServerInfo info, Disconnect disconnect) {
public void handleConnectionException(RegisteredServer server, Disconnect disconnect) {
Component disconnectReason = ComponentSerializers.JSON.deserialize(disconnect.getReason());
String plainTextReason = PASS_THRU_TRANSLATE.serialize(disconnectReason);
if (connectedServer != null && connectedServer.getServerInfo().equals(info)) {
logger.error("{}: kicked from server {}: {}", this, info.getName(), plainTextReason);
handleConnectionException(info, disconnectReason, TextComponent.builder()
.content("Kicked from " + info.getName() + ": ")
if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) {
logger.error("{}: kicked from server {}: {}", this, server.getServerInfo().getName(), plainTextReason);
handleConnectionException(server, disconnectReason, TextComponent.builder()
.content("Kicked from " + server.getServerInfo().getName() + ": ")
.color(TextColor.RED)
.append(disconnectReason)
.build());
} else {
logger.error("{}: disconnected while connecting to {}: {}", this, info.getName(), plainTextReason);
handleConnectionException(info, disconnectReason, TextComponent.builder()
.content("Unable to connect to " + info.getName() + ": ")
logger.error("{}: disconnected while connecting to {}: {}", this, server.getServerInfo().getName(), plainTextReason);
handleConnectionException(server, disconnectReason, TextComponent.builder()
.content("Unable to connect to " + server.getServerInfo().getName() + ": ")
.color(TextColor.RED)
.append(disconnectReason)
.build());
}
}
private void handleConnectionException(ServerInfo info, @Nullable Component kickReason, Component friendlyReason) {
boolean alreadyConnected = connectedServer != null && connectedServer.getServerInfo().equals(info);;
private void handleConnectionException(RegisteredServer rs, @Nullable Component kickReason, Component friendlyReason) {
boolean alreadyConnected = connectedServer != null && connectedServer.getServerInfo().equals(rs.getServerInfo());
connectionInFlight = null;
if (connectedServer == null) {
// The player isn't yet connected to a server.
Optional<ServerInfo> nextServer = getNextServerToTry();
Optional<RegisteredServer> nextServer = getNextServerToTry();
if (nextServer.isPresent()) {
createConnectionRequest(nextServer.get()).fireAndForget();
} else {
connection.closeWith(Disconnect.create(friendlyReason));
}
} else if (connectedServer.getServerInfo().equals(info)) {
} else if (connectedServer.getServerInfo().equals(rs.getServerInfo())) {
// Already connected to the server being disconnected from.
if (kickReason != null) {
server.getEventManager().fire(new KickedFromServerEvent(this, info, kickReason, !alreadyConnected, friendlyReason))
server.getEventManager().fire(new KickedFromServerEvent(this, rs, kickReason, !alreadyConnected, friendlyReason))
.thenAcceptAsync(event -> {
if (event.getResult() instanceof KickedFromServerEvent.DisconnectPlayer) {
KickedFromServerEvent.DisconnectPlayer res = (KickedFromServerEvent.DisconnectPlayer) event.getResult();
@ -255,7 +257,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
}
Optional<ServerInfo> getNextServerToTry() {
Optional<RegisteredServer> getNextServerToTry() {
List<String> serversToTry = server.getConfiguration().getAttemptConnectionOrder();
if (tryIndex >= serversToTry.size()) {
return Optional.empty();
@ -289,7 +291,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
);
}
return new VelocityServerConnection(newEvent.getResult().getServer().get(), this, server).connect();
RegisteredServer rs = newEvent.getResult().getServer().get();
Preconditions.checkState(rs instanceof VelocityRegisteredServer, "Not a valid Velocity server.");
return new VelocityServerConnection((VelocityRegisteredServer) rs, this, server).connect();
});
}
@ -335,25 +339,26 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public void sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
Preconditions.checkNotNull(identifier, "identifier");
Preconditions.checkNotNull(data, "data");
PluginMessage message = new PluginMessage();
message.setChannel(identifier.getId());
message.setData(data);
connection.write(message);
return true;
}
private class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder {
private final ServerInfo info;
private final RegisteredServer server;
ConnectionRequestBuilderImpl(ServerInfo info) {
this.info = Preconditions.checkNotNull(info, "info");
ConnectionRequestBuilderImpl(RegisteredServer server) {
this.server = Preconditions.checkNotNull(server, "info");
}
@Override
public ServerInfo getServer() {
return info;
public RegisteredServer getServer() {
return server;
}
@Override
@ -366,7 +371,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
connect()
.whenCompleteAsync((status, throwable) -> {
if (throwable != null) {
handleConnectionException(info, throwable);
handleConnectionException(server, throwable);
return;
}
@ -381,7 +386,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
// Ignored; the plugin probably already handled this.
break;
case SERVER_DISCONNECTED:
handleConnectionException(info, Disconnect.create(status.getReason().orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR)));
handleConnectionException(server, Disconnect.create(status.getReason().orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR)));
break;
}
}, connection.getChannel().eventLoop());

Datei anzeigen

@ -8,6 +8,7 @@ import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentR
import com.velocitypowered.api.event.permission.PermissionsSetupEvent;
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
import com.velocitypowered.api.proxy.InboundConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
import com.velocitypowered.proxy.connection.VelocityConstants;
@ -220,7 +221,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
}
private void handleProxyLogin(ConnectedPlayer player) {
Optional<ServerInfo> toTry = player.getNextServerToTry();
Optional<RegisteredServer> toTry = player.getNextServerToTry();
if (!toTry.isPresent()) {
player.close(TextComponent.of("No available servers", TextColor.RED));
return;
@ -246,9 +247,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
logger.info("{} has connected", player);
inbound.setSessionHandler(new InitialConnectSessionHandler(player));
server.getEventManager().fire(new PostLoginEvent(player)).thenRun(() -> {
player.createConnectionRequest(toTry.get()).fireAndForget();
});
server.getEventManager().fire(new PostLoginEvent(player)).thenRun(() -> player.createConnectionRequest(toTry.get()).fireAndForget());
}
@Override

Datei anzeigen

@ -0,0 +1,68 @@
package com.velocitypowered.proxy.server;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.proxy.VelocityServer;
import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ServerMap {
private final VelocityServer server;
private final Map<String, RegisteredServer> servers = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public ServerMap(VelocityServer server) {
this.server = server;
}
public Optional<RegisteredServer> getServer(String name) {
Preconditions.checkNotNull(name, "server");
String lowerName = name.toLowerCase(Locale.US);
lock.readLock().lock();
try {
return Optional.ofNullable(servers.get(lowerName));
} finally {
lock.readLock().unlock();
}
}
public Collection<RegisteredServer> getAllServers() {
lock.readLock().lock();
try {
return ImmutableList.copyOf(servers.values());
} finally {
lock.readLock().unlock();
}
}
public RegisteredServer register(ServerInfo serverInfo) {
Preconditions.checkNotNull(serverInfo, "serverInfo");
String lowerName = serverInfo.getName().toLowerCase(Locale.US);
lock.writeLock().lock();
try {
VelocityRegisteredServer rs = new VelocityRegisteredServer(server, serverInfo);
Preconditions.checkArgument(servers.putIfAbsent(lowerName, rs) == null, "Server with name %s already registered", serverInfo.getName());
return rs;
} finally {
lock.writeLock().unlock();
}
}
public void unregister(ServerInfo serverInfo) {
Preconditions.checkNotNull(serverInfo, "serverInfo");
String lowerName = serverInfo.getName().toLowerCase(Locale.US);
lock.writeLock().lock();
try {
RegisteredServer rs = servers.get(lowerName);
Preconditions.checkArgument(rs != null, "Server with name %s is not registered!", serverInfo.getName());
Preconditions.checkArgument(rs.getServerInfo().equals(serverInfo), "Trying to remove server %s with differing information", serverInfo.getName());
servers.remove(lowerName);
} finally {
lock.writeLock().unlock();
}
}
}

Datei anzeigen

@ -0,0 +1,95 @@
package com.velocitypowered.proxy.server;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class VelocityRegisteredServer implements RegisteredServer {
private final VelocityServer server;
private final ServerInfo serverInfo;
private final Set<ConnectedPlayer> players = new HashSet<>();
private final ReadWriteLock playersLock = new ReentrantReadWriteLock();
public VelocityRegisteredServer(VelocityServer server, ServerInfo serverInfo) {
this.server = server;
this.serverInfo = serverInfo;
}
@Override
public ServerInfo getServerInfo() {
return serverInfo;
}
@Override
public Collection<Player> getPlayersConnected() {
playersLock.readLock().lock();
try {
return ImmutableList.copyOf(players);
} finally {
playersLock.readLock().unlock();
}
}
@Override
public CompletableFuture<ServerPing> ping() {
CompletableFuture<ServerPing> p = new CompletableFuture<>();
p.completeExceptionally(new UnsupportedOperationException("Not currently implemented."));
return p;
}
public void addPlayer(ConnectedPlayer player) {
playersLock.writeLock().lock();
try {
players.add(player);
} finally {
playersLock.writeLock().unlock();
}
}
public void removePlayer(ConnectedPlayer player) {
playersLock.writeLock().lock();
try {
players.remove(player);
} finally {
playersLock.writeLock().unlock();
}
}
@Override
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
ServerConnection backendConnection = null;
playersLock.readLock().lock();
try {
for (ConnectedPlayer player : players) {
if (player.getConnectedServer() != null && player.getConnectedServer().getServerInfo().equals(serverInfo)) {
backendConnection = player.getConnectedServer();
break;
}
}
if (backendConnection == null) {
return false;
}
} finally {
playersLock.readLock().unlock();
}
return backendConnection.sendPluginMessage(identifier, data);
}
@Override
public String toString() {
return "registered server: " + serverInfo;
}
}

Datei anzeigen

@ -1,56 +0,0 @@
package com.velocitypowered.proxy.util;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.proxy.server.ServerInfo;
import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ServerMap {
private final Map<String, ServerInfo> servers = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public Optional<ServerInfo> getServer(String server) {
Preconditions.checkNotNull(server, "server");
String lowerName = server.toLowerCase(Locale.US);
lock.readLock().lock();
try {
return Optional.ofNullable(servers.get(lowerName));
} finally {
lock.readLock().unlock();
}
}
public Collection<ServerInfo> getAllServers() {
lock.readLock().lock();
try {
return ImmutableList.copyOf(servers.values());
} finally {
lock.readLock().unlock();
}
}
public void register(ServerInfo server) {
Preconditions.checkNotNull(server, "server");
String lowerName = server.getName().toLowerCase(Locale.US);
lock.writeLock().lock();
try {
Preconditions.checkArgument(servers.putIfAbsent(lowerName, server) == null, "Server with name %s already registered", server.getName());
} finally {
lock.writeLock().unlock();
}
}
public void unregister(ServerInfo server) {
Preconditions.checkNotNull(server, "server");
String lowerName = server.getName().toLowerCase(Locale.US);
lock.writeLock().lock();
try {
Preconditions.checkArgument(servers.remove(lowerName, server), "Server with this name is not registered!");
} finally {
lock.writeLock().unlock();
}
}
}

Datei anzeigen

@ -1,6 +1,8 @@
package com.velocitypowered.proxy.util;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.proxy.server.ServerMap;
import org.junit.jupiter.api.Test;
import java.net.InetAddress;
@ -14,18 +16,18 @@ class ServerMapTest {
@Test
void respectsCaseInsensitivity() {
ServerMap map = new ServerMap();
ServerMap map = new ServerMap(null);
ServerInfo info = new ServerInfo("TestServer", TEST_ADDRESS);
map.register(info);
RegisteredServer connection = map.register(info);
assertEquals(Optional.of(info), map.getServer("TestServer"));
assertEquals(Optional.of(info), map.getServer("testserver"));
assertEquals(Optional.of(info), map.getServer("TESTSERVER"));
assertEquals(Optional.of(connection), map.getServer("TestServer"));
assertEquals(Optional.of(connection), map.getServer("testserver"));
assertEquals(Optional.of(connection), map.getServer("TESTSERVER"));
}
@Test
void rejectsRepeatedRegisterAttempts() {
ServerMap map = new ServerMap();
ServerMap map = new ServerMap(null);
ServerInfo info = new ServerInfo("TestServer", TEST_ADDRESS);
map.register(info);