node);
diff --git a/api/src/main/java/com/velocitypowered/api/command/InvocableCommand.java b/api/src/main/java/com/velocitypowered/api/command/InvocableCommand.java
index e6183e52d..b5301ae3b 100644
--- a/api/src/main/java/com/velocitypowered/api/command/InvocableCommand.java
+++ b/api/src/main/java/com/velocitypowered/api/command/InvocableCommand.java
@@ -14,6 +14,11 @@ import java.util.concurrent.CompletableFuture;
/**
* A command that can be executed with arbitrary arguments.
*
+ * Modifying the command tree (e.g. registering a command via
+ * {@link CommandManager#register(CommandMeta, Command)}) during
+ * permission checking and suggestions provision results in
+ * undefined behavior, which may include deadlocks.
+ *
* @param the type of the command invocation object
*/
public interface InvocableCommand> extends Command {
diff --git a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java
index e16bb0673..1fd5d7cfe 100644
--- a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java
+++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java
@@ -106,6 +106,14 @@ public interface ProxyServer extends Audience {
*/
Collection matchServer(String partialName);
+ /**
+ * Creates a raw {@link RegisteredServer} without tying it into the internal server map.
+ *
+ * @param server the server to register
+ * @return the {@link RegisteredServer} implementation created by the provided {@link ServerInfo}.
+ */
+ RegisteredServer createRawRegisteredServer(ServerInfo server);
+
/**
* Registers a server with this proxy. A server with this name should not already exist.
*
diff --git a/proxy/build.gradle b/proxy/build.gradle
index a206dcf39..27431da34 100644
--- a/proxy/build.gradle
+++ b/proxy/build.gradle
@@ -28,7 +28,6 @@ jar {
attributes 'Implementation-Version': version
attributes 'Implementation-Vendor': "Velocity Contributors"
attributes 'Multi-Release': 'true'
- attributes 'Add-Opens': 'java.base/java.lang'
}
}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
index 1dd4e8a35..34b7d966a 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
@@ -602,6 +602,11 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
return servers.getAllServers();
}
+ @Override
+ public RegisteredServer createRawRegisteredServer(ServerInfo server) {
+ return servers.createRawRegisteredServer(server);
+ }
+
@Override
public RegisteredServer registerServer(ServerInfo server) {
return servers.register(server);
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java
index 9e2c36562..1f30633c5 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java
@@ -985,7 +985,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
&& !connectedServer.hasCompletedJoin())) {
return Optional.of(ConnectionRequestBuilder.Status.CONNECTION_IN_PROGRESS);
}
- if (connectedServer != null && connectedServer.getServer().equals(server)) {
+ if (connectedServer != null
+ && connectedServer.getServer().getServerInfo().equals(server.getServerInfo())) {
return Optional.of(ALREADY_CONNECTED);
}
return Optional.empty();
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java b/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java
index 41d337be0..00fc2edd6 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java
@@ -54,6 +54,17 @@ public class ServerMap {
return ImmutableList.copyOf(servers.values());
}
+ /**
+ * Creates a raw implementation of a {@link RegisteredServer} without
+ * tying it to the internal server map.
+ *
+ * @param serverInfo the server to create a registered server with
+ * @return the {@link RegisteredServer} built from the {@link ServerInfo}
+ */
+ public RegisteredServer createRawRegisteredServer(ServerInfo serverInfo) {
+ return new VelocityRegisteredServer(server, serverInfo);
+ }
+
/**
* Registers a server with the proxy.
*
@@ -63,7 +74,7 @@ public class ServerMap {
public RegisteredServer register(ServerInfo serverInfo) {
Preconditions.checkNotNull(serverInfo, "serverInfo");
String lowerName = serverInfo.getName().toLowerCase(Locale.US);
- VelocityRegisteredServer rs = new VelocityRegisteredServer(server, serverInfo);
+ RegisteredServer rs = createRawRegisteredServer(serverInfo);
RegisteredServer existing = servers.putIfAbsent(lowerName, rs);
if (existing != null && !existing.getServerInfo().equals(serverInfo)) {