Feature: per-server info forwarding mode#1655
Feature: per-server info forwarding mode#1655MarcoLvr wants to merge 11 commits intoPaperMC:dev/3.0.0from
Conversation
What's this?
Every server can use a different forwarding mode to obtain and forward player info.
For instance, if you are running a 1.12 (or lower version) server on a velocity proxy with MODERN player info forwarding the server doesn't support MODERN forwarding. So you need to set LEGACY forwarding mode for that server and velocity will use ONLY FOR THAT SERVER the legacy forwarding mode. The default mode is "FOLLOWUP" mode which uses the player info forwarding mode set in the config.
The main configuration now supports two syntaxes for describing a server:
classic syntax (FOLLOWUP mode): server = "address:port"
new syntax: server = {address = "address:port", forwarding-mode = "MODE"}
all servers created without defining a forwarding mode will use the FOLLOWUP mode.
api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java
Outdated
Show resolved
Hide resolved
api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfoForwardingMode.java
Outdated
Show resolved
Hide resolved
proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java
Show resolved
Hide resolved
api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfoForwardingMode.java
Show resolved
Hide resolved
proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java
Show resolved
Hide resolved
…Servers() method to avoid breaking compatibility with current versions. ServerInfoForwardingMode.java: added a javadoc for the FOLLOWUP mode and added the NONE mode VelocityConfiguration.java: made "mode" field optional for the new server syntax. Restored old forwarding secret check. VelocityRegisteredServer.java: added the forwarding secret check if forwarding mode is MODERN or BUNGEEGUARD ServerForwardingModeUtil.java: mapped NONE mode to NONE mode in PlayerInfoForwarding.java
proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java
Outdated
Show resolved
Hide resolved
proxy/src/main/java/com/velocitypowered/proxy/config/server/BackendServerConfigImpl.java
Outdated
Show resolved
Hide resolved
…guration#validate added requiresNonNull checks on BackendServerConfigImpl
api/src/main/java/com/velocitypowered/api/proxy/config/BackendServerConfig.java
Outdated
Show resolved
Hide resolved
proxy/src/main/java/com/velocitypowered/proxy/ProxyOptions.java
Outdated
Show resolved
Hide resolved
proxy/src/main/java/com/velocitypowered/proxy/util/ServerForwardingModeUtil.java
Outdated
Show resolved
Hide resolved
…rverConfig. Made address and forwardingMode final in its implementation class. deprecated getServers() in favor of getBackendServers() Fixed proxy option syntax for "add-server". Now the syntax is: <name>:<host>:[port]:[forwardingmode] moved ServerInfoForwardingMode conversion from util package to VelocityRegisteredServer#getConfiguredPlayerInfoForwarding
|
did everything! Let me know if there's any other change to do |
api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java
Outdated
Show resolved
Hide resolved
proxy/src/main/java/com/velocitypowered/proxy/config/server/BackendServerConfigImpl.java
Outdated
Show resolved
Hide resolved
…mentation class in the proxy module. Modified comment in ProxyConfig#getServers
Re-added null checks
|
@Emilxyz we are waiting for you :) |
Emilxyz
left a comment
There was a problem hiding this comment.
we are waiting for you
I'm not a team member, just sharing my thoughts, so my approval is not actually required. Also a friendly reminder that everyone is doing this on their free time, so it's not really needed to bump a review request after only a day.
I gave this another look and it basically lgtm other than these minor things in review comments.
Another thought I had (but I would wait with doing any changes) is that maybe we can just use ServerInfo for this instead of adding a new type, they basically share the same fields and probably will continue to do so in the future as well.
Also there is the issue of the planned config api switch which is holding up other things already that would make changes to the config, so be prepared for this pr to also be affected by this
api/src/main/java/com/velocitypowered/api/proxy/config/BackendServerConfig.java
Outdated
Show resolved
Hide resolved
api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfoForwardingMode.java
Outdated
Show resolved
Hide resolved
proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java
Outdated
Show resolved
Hide resolved
proxy/src/main/java/com/velocitypowered/proxy/ProxyOptions.java
Outdated
Show resolved
Hide resolved
api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfoForwardingMode.java
Outdated
Show resolved
Hide resolved
api/src/main/java/com/velocitypowered/api/proxy/config/BackendServerConfig.java
Show resolved
Hide resolved
api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfo.java
Outdated
Show resolved
Hide resolved
api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfo.java
Outdated
Show resolved
Hide resolved
api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfo.java
Outdated
Show resolved
Hide resolved
proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
This PR introduces per-server player info forwarding mode configuration, allowing each backend server to use a different forwarding mode (MODERN, LEGACY, BUNGEEGUARD, or NONE) instead of inheriting the global proxy-wide setting. This enables mixed-version networks where some servers (e.g., 1.12) require LEGACY forwarding while others (e.g., Fabric 1.21) require MODERN forwarding.
Changes:
- Added new
ServerInfoForwardingModeenum andBackendServerConfigrecord to the API for per-server configuration - Extended configuration syntax to support both classic string format (
server = "address:port") and new object format (server = {address = "...", forwarding-mode = "..."}) - Modified
ServerInfoto include an optional forwarding mode field, with corresponding updates to equals/hashCode/toString methods
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfoForwardingMode.java | New enum defining per-server forwarding modes (MODERN, BUNGEEGUARD, LEGACY, NONE) |
| api/src/main/java/com/velocitypowered/api/proxy/config/BackendServerConfig.java | New record to hold backend server configuration including address and optional forwarding mode |
| api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfo.java | Added forwarding mode field, new constructor, getter method, and updated equals/hashCode/toString |
| api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java | Added getBackendServers() method and deprecated getServers() |
| proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java | Updated server parsing to support both old and new config formats, added per-server forwarding secret validation |
| proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java | Added getConfiguredPlayerInfoForwarding() to resolve effective forwarding mode |
| proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java | Changed to use per-server forwarding mode instead of global setting |
| proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java | Updated to use per-server forwarding mode in login flow |
| proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java | Updated server registration to include forwarding mode |
| proxy/src/main/java/com/velocitypowered/proxy/ProxyOptions.java | Extended command-line server parsing to support forwarding mode parameter |
| proxy/src/main/resources/default-velocity.toml | Updated example configuration to demonstrate new syntax |
Comments suppressed due to low confidence (4)
api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfoForwardingMode.java:19
- The enum constant order in ServerInfoForwardingMode is inconsistent with PlayerInfoForwarding. ServerInfoForwardingMode orders them as MODERN, BUNGEEGUARD, LEGACY, NONE while PlayerInfoForwarding uses NONE, LEGACY, BUNGEEGUARD, MODERN. For consistency and to avoid confusion when developers work with both enums, consider using the same ordering as PlayerInfoForwarding since these represent the same concepts.
public enum ServerInfoForwardingMode {
MODERN,
BUNGEEGUARD,
LEGACY,
NONE
proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java:660
- The configuration parser silently ignores unknown keys in the server configuration object. If a user makes a typo like "forwaring-mode" or "forwarding_mode", the configuration will load without error but the forwarding mode will be null (inheriting from global config). This could lead to unexpected behavior. Consider logging a warning for unknown configuration keys within server entries to help users identify typos or deprecated options.
for (UnmodifiableConfig.Entry entry2 : c.entrySet()) {
if (entry2.getKey().equalsIgnoreCase("address")) {
address = entry2.getValue();
}
if (entry2.getKey().equalsIgnoreCase("forwarding-mode")) {
forwardingMode = ServerInfoForwardingMode.valueOf(ServerInfoForwardingMode.class, entry2.getValue());
}
}
api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfoForwardingMode.java:21
- There are two blank lines at the end of the file instead of one. This is inconsistent with typical Java file formatting conventions which usually have exactly one blank line at the end of the file.
proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java:82 - The javadoc has an extra blank line between the two
@paramtags (line 81), which is inconsistent with standard javadoc formatting where@paramtags should be consecutive without blank lines between them.
* @param server velocity instance
*
* @param serverInfo info of the backend server
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| logger.error("You don't have a forwarding secret set. This is required if " | ||
| + "you are using MODERN or BUNGEEGUARD forwarding modes."); |
There was a problem hiding this comment.
The error message says "You don't have a forwarding secret set. This is required if you are using MODERN or BUNGEEGUARD forwarding modes." but it doesn't specify which server has the problematic configuration. Consider including the server name (entry.getKey()) in the error message to help administrators identify which specific server needs attention. For example: "Server 'servername' uses MODERN/BUNGEEGUARD forwarding mode but you don't have a forwarding secret set."
| logger.error("You don't have a forwarding secret set. This is required if " | |
| + "you are using MODERN or BUNGEEGUARD forwarding modes."); | |
| logger.error("Server '{}' uses MODERN or BUNGEEGUARD forwarding mode but you " | |
| + "don't have a forwarding secret set. This is required for security.", | |
| entry.getKey()); |
| factions = {address = "127.0.0.1:30067", forwarding-mode = "MODERN"} | ||
| minigames = {address = "127.0.0.1:30068", forwarding-mode = "LEGACY"} |
There was a problem hiding this comment.
The example values in the default configuration show forwarding modes that may not align with a typical setup. Setting "factions" to MODERN and "minigames" to LEGACY seems arbitrary. Consider either using all three servers with null/INHERIT mode (demonstrating the default behavior), or adding comments to explain why different modes are shown. This will help users understand when and why they should set per-server forwarding modes rather than just copying these examples.
| @@ -125,7 +138,7 @@ public Class<? extends ServerInfo> valueType() { | |||
|
|
|||
| @Override | |||
| public String valuePattern() { | |||
| return "name>:<address"; | |||
| return "name>:<host>:[port]:[forwardingmode]"; | |||
There was a problem hiding this comment.
The valuePattern indicates optional components with brackets "[port]:[forwardingmode]", but the conversion logic at line 109 splits on ":" with a limit of 4, expecting exactly 4 parts for the full format. The description says "Use ::[port]:[forwardingmode]" which suggests port and forwardingmode are optional, but the actual parsing logic requires that if you want to specify forwardingmode, you must include all 4 parts (name, host, port, forwardingmode). The pattern and error message should clarify this or the parsing should be made more flexible to handle name:host:forwardingmode without requiring the port.
| return Objects.equals(name, that.name) | ||
| && Objects.equals(address, that.address); | ||
| && Objects.equals(address, that.address) | ||
| && Objects.equals(forwardingMode, that.forwardingMode); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(name, address); | ||
| return Objects.hash(name, address, forwardingMode); |
There was a problem hiding this comment.
The change to include forwardingMode in equals() and hashCode() is a breaking change in the API. Previously, two ServerInfo objects with the same name and address but different (or null vs non-null) forwardingMode would be considered equal. Now they won't be. This could break existing code that relies on ServerInfo equality for lookups, comparisons, or use in collections. While this change makes logical sense for the new feature, it should be clearly documented as a breaking change or reconsidered to maintain backward compatibility. Consider whether forwardingMode should be part of the identity of a ServerInfo object or just a configuration detail.
| address = entry2.getValue(); | ||
| } | ||
| if (entry2.getKey().equalsIgnoreCase("forwarding-mode")) { | ||
| forwardingMode = ServerInfoForwardingMode.valueOf(ServerInfoForwardingMode.class, entry2.getValue()); |
There was a problem hiding this comment.
The valueOf call has several issues: 1) It uses the two-parameter Enum.valueOf(Class, String) method but should use the simpler ServerInfoForwardingMode.valueOf(String) method for consistency with line 126 in ProxyOptions.java. 2) The value is not converted to uppercase before calling valueOf, making configuration case-sensitive. Users writing "modern" or "legacy" will get an IllegalArgumentException. 3) There's no try-catch block to handle invalid values gracefully. The code should convert the value to uppercase using entry2.getValue().toString().toUpperCase() and wrap the valueOf call in a try-catch block with a meaningful error message including the server name.
| forwardingMode = ServerInfoForwardingMode.valueOf(ServerInfoForwardingMode.class, entry2.getValue()); | |
| Object rawValue = entry2.getValue(); | |
| if (rawValue != null) { | |
| String modeValue = rawValue.toString().toUpperCase(); | |
| try { | |
| forwardingMode = ServerInfoForwardingMode.valueOf(modeValue); | |
| } catch (IllegalArgumentException ex) { | |
| throw new IllegalArgumentException( | |
| "Invalid forwarding-mode '" + rawValue + "' for server '" + entry.getKey() + "'", ex); | |
| } | |
| } |
| */ | ||
| public PlayerInfoForwarding getConfiguredPlayerInfoForwarding() { | ||
| if (serverInfo.getServerInfoForwardingMode() == null) { | ||
| return server.getConfiguration().getPlayerInfoForwardingMode(); |
There was a problem hiding this comment.
The method has a potential NullPointerException at line 101. If serverInfo.getServerInfoForwardingMode() returns null (line 100 check), then server.getConfiguration() is called. However, the server field is annotated with @Nullable and could be null. When VelocityRegisteredServer is constructed with a null server (which is allowed by the constructor), calling getConfiguration() on it will throw a NullPointerException. Consider adding a null check for server or documenting that this method requires a non-null server to function properly.
| return server.getConfiguration().getPlayerInfoForwardingMode(); | |
| VelocityServer velocityServer = requireNonNull(server, "server"); | |
| return velocityServer.getConfiguration().getPlayerInfoForwardingMode(); |
| # Configure your servers here. Each key represents the server's name, and the value | ||
| # represents the IP address of the server to connect to. |
There was a problem hiding this comment.
The comment on lines 78-79 doesn't explain the new per-server forwarding mode feature. Users seeing the new syntax for the first time won't understand what "forwarding-mode" means or when they should use it. Consider expanding the comment to briefly mention both syntaxes and that forwarding-mode is optional, with a reference to the documentation or a note that null/omitted values inherit from the global player-info-forwarding-mode setting.
| # Configure your servers here. Each key represents the server's name, and the value | |
| # represents the IP address of the server to connect to. | |
| # Configure your servers here. Each key represents the server's name. | |
| # You can use the short form, where the value is the IP address and port to connect to: | |
| # lobby = "127.0.0.1:30066" | |
| # or the object form: | |
| # factions = { address = "127.0.0.1:30067", forwarding-mode = "MODERN" } | |
| # In the object form, address is the IP/port and forwarding-mode is optional; if it is | |
| # omitted or set to null, the server inherits the global player-info-forwarding-mode | |
| # setting defined above. See the Velocity documentation for more details. |
What's this?
With this edit every server can use a different forwarding mode to obtain and forward player info. For instance, if you are running a 1.12 (or lower version) server on a velocity proxy with MODERN player info forwarding the server doesn't support MODERN forwarding. So you can set LEGACY forwarding mode for that server and velocity will use ONLY FOR THAT SERVER the legacy forwarding mode. The default mode is "INHERIT" which uses the player info forwarding mode set in the config.
Note: the INHERIT mode doesn't physically exists. Just set the server forwarding mode to NULL or avoid valuing it in the config
The main configuration now supports two syntaxes for describing a server:
classic syntax (INHERIT mode):
server = "address:port"new syntax:
server = {address = "address:port", forwarding-mode = "MODE"}all servers created without defining a forwarding mode will use the INHERIT mode.
Example of use
Imagine a simple network with a lobby, a 1.8 game mode (like bedwars) and maybe a Fabric 1.21 server.
Fabric needs MODERN forwarding but the 1.8 server cannot use it. So you have two choices:
This system could be very useful for these types of situations!