Update to 26.3 #4

Merged
YoyoNow merged 21 commits from update into master 2026-09-24 17:09:44 +02:00
6 changed files with 228 additions and 80 deletions
Showing only changes of commit 21bbf35d1d - Show all commits
@@ -16,13 +16,49 @@
*/ */
package com.velocitypowered.proxy.config; package com.velocitypowered.proxy.config;
import com.electronwill.nightconfig.core.Config;
/** /**
* Supported passthrough modes for ping passthrough. * Object to contain all the things that can be toggled for ping passthrough.
*
* @param version Whether the version should be passed through.
* @param players Whether the player count should be passed through.
* @param description Whether the description should be passed through.
* @param favicon Whether the favicon should be passed through.
* @param modinfo Whether the modinfo should be passed through.
*/ */
public enum PingPassthroughMode { public record PingPassthroughMode(boolean version, boolean players,
DISABLED, boolean description, boolean favicon, boolean modinfo) {
MODS, public static final PingPassthroughMode DEFAULT = new PingPassthroughMode();
DESCRIPTION,
ALL /**
* Creates a default PingPassthroughMode.
*/
private PingPassthroughMode() {
this(false, false, false, false, false);
}
/**
* Returns a PingPassthroughMode from a config section, or the default if the section is null.
* Based on the code for PacketLimiterConfig.
*
* @param config The configuration object to parse.
* @return The PingPassthroughMode, or the default if {@code config} is null.
*/
public static PingPassthroughMode fromConfig(Config config) {
if (config == null) {
return DEFAULT;
}
return new PingPassthroughMode(
config.getOrElse("version", DEFAULT.version()),
config.getOrElse("players", DEFAULT.players()),
config.getOrElse("description", DEFAULT.description()),
config.getOrElse("favicon", DEFAULT.favicon()),
config.getOrElse("modinfo", DEFAULT.modinfo()));
}
public boolean enabled() {
return this.version || this.players || this.description || this.favicon
|| this.modinfo;
}
} }
@@ -32,6 +32,7 @@ import com.velocitypowered.proxy.config.migration.KeyAuthenticationMigration;
import com.velocitypowered.proxy.config.migration.MiniMessageTranslationsMigration; import com.velocitypowered.proxy.config.migration.MiniMessageTranslationsMigration;
import com.velocitypowered.proxy.config.migration.MotdMigration; import com.velocitypowered.proxy.config.migration.MotdMigration;
import com.velocitypowered.proxy.config.migration.PacketLimiterMigration; import com.velocitypowered.proxy.config.migration.PacketLimiterMigration;
import com.velocitypowered.proxy.config.migration.PingPassthroughMigration;
import com.velocitypowered.proxy.config.migration.TransferIntegrationMigration; import com.velocitypowered.proxy.config.migration.TransferIntegrationMigration;
import com.velocitypowered.proxy.util.AddressUtil; import com.velocitypowered.proxy.util.AddressUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -79,7 +80,7 @@ public class VelocityConfiguration implements ProxyConfig {
@Expose @Expose
private boolean onlineModeKickExistingPlayers = false; private boolean onlineModeKickExistingPlayers = false;
@Expose @Expose
private PingPassthroughMode pingPassthrough = PingPassthroughMode.DISABLED; private PingPassthroughMode pingPassthrough = PingPassthroughMode.DEFAULT;
@Expose @Expose
private boolean samplePlayersInPing = false; private boolean samplePlayersInPing = false;
private final Servers servers; private final Servers servers;
@@ -513,7 +514,8 @@ public class VelocityConfiguration implements ProxyConfig {
new MotdMigration(), new MotdMigration(),
new MiniMessageTranslationsMigration(), new MiniMessageTranslationsMigration(),
new TransferIntegrationMigration(), new TransferIntegrationMigration(),
new PacketLimiterMigration() new PacketLimiterMigration(),
new PingPassthroughMigration(),
}; };
for (final ConfigurationMigration migration : migrations) { for (final ConfigurationMigration migration : migrations) {
@@ -555,9 +557,7 @@ public class VelocityConfiguration implements ProxyConfig {
final CommentedConfig metricsConfig = config.get("metrics"); final CommentedConfig metricsConfig = config.get("metrics");
final PlayerInfoForwarding forwardingMode = config.getEnumOrElse( final PlayerInfoForwarding forwardingMode = config.getEnumOrElse(
"player-info-forwarding-mode", PlayerInfoForwarding.NONE); "player-info-forwarding-mode", PlayerInfoForwarding.NONE);
final PingPassthroughMode pingPassthroughMode = config.getEnumOrElse("ping-passthrough", final PingPassthroughMode pingPassthrough = PingPassthroughMode.fromConfig(config.get("ping-passthrough"));
PingPassthroughMode.DISABLED);
final boolean samplePlayersInPing = config.getOrElse("sample-players-in-ping", false); final boolean samplePlayersInPing = config.getOrElse("sample-players-in-ping", false);
final String bind = config.getOrElse("bind", "0.0.0.0:25565"); final String bind = config.getOrElse("bind", "0.0.0.0:25565");
@@ -590,7 +590,7 @@ public class VelocityConfiguration implements ProxyConfig {
forwardingMode, forwardingMode,
forwardingSecret, forwardingSecret,
kickExisting, kickExisting,
pingPassthroughMode, pingPassthrough,
samplePlayersInPing, samplePlayersInPing,
enablePlayerAddressLogging, enablePlayerAddressLogging,
new Servers(serversConfig), new Servers(serversConfig),
@@ -30,7 +30,8 @@ public sealed interface ConfigurationMigration
MotdMigration, MotdMigration,
MiniMessageTranslationsMigration, MiniMessageTranslationsMigration,
TransferIntegrationMigration, TransferIntegrationMigration,
PacketLimiterMigration { PacketLimiterMigration,
PingPassthroughMigration {
boolean shouldMigrate(CommentedFileConfig config); boolean shouldMigrate(CommentedFileConfig config);
void migrate(CommentedFileConfig config, Logger logger) throws IOException; void migrate(CommentedFileConfig config, Logger logger) throws IOException;
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2024-2026 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.config.migration;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import java.util.Locale;
import org.apache.logging.log4j.Logger;
/**
* Migrate the old ping passthrough entry to separate config entries.
*/
public final class PingPassthroughMigration implements ConfigurationMigration {
@Override
public boolean shouldMigrate(final CommentedFileConfig config) {
return configVersion(config) < 2.9;
}
@Override
public void migrate(final CommentedFileConfig config, final Logger logger) {
// Get legacy ping passthrough value
final String legacyMode = config.getOrElse("ping-passthrough", "DISABLED")
.toUpperCase(Locale.ROOT);
boolean version = false;
boolean players = false;
boolean description = false;
boolean favicon = false;
boolean modinfo = false;
switch (legacyMode) {
case "ALL":
version = true;
players = true;
description = true;
favicon = true;
modinfo = true;
break;
case "DESCRIPTION":
description = true;
modinfo = true;
break;
case "MODS":
modinfo = true;
break;
default: // DISABLED
break;
}
config.removeComment("ping-passthrough");
config.remove("ping-passthrough");
config.setComment("announce-forge",
" Announce whether or not your server supports Forge. If you run a modded server, we\n"
+ " suggest turning this on.\n"
+ "\n"
+ " If your network runs one modpack consistently, consider using ping-passthrough.modinfo = true\n"
+ " instead for a nicer display in the server list.");
config.set("ping-passthrough.version", version);
config.setComment(
"ping-passthrough.version",
" Should Velocity pass the version number from the backend server when responding to server list ping requests?"
);
config.set("ping-passthrough.players", players);
config.setComment(
"ping-passthrough.players",
" Should Velocity pass the player count from the backend server when responding to server list ping requests?"
);
config.set("ping-passthrough.description", description);
config.setComment(
"ping-passthrough.description",
" Should Velocity pass the description from the backend server when responding to server list ping requests?"
);
config.set("ping-passthrough.favicon", favicon);
config.setComment(
"ping-passthrough.favicon",
" Should Velocity pass the favicon (also known as the server icon) from the backend server when responding to server list ping requests?"
);
config.set("ping-passthrough.modinfo", modinfo);
config.setComment(
"ping-passthrough.modinfo",
" Should Velocity pass the mod list from the backend server when responding to server list ping requests?"
);
config.set("config-version", "2.9");
}
}
@@ -23,6 +23,7 @@ import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.server.PingOptions; import com.velocitypowered.api.proxy.server.PingOptions;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.api.util.Favicon;
import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.config.PingPassthroughMode; import com.velocitypowered.proxy.config.PingPassthroughMode;
@@ -100,60 +101,68 @@ public class ServerListPingHandler {
CompletableFuture<List<ServerPing>> pingResponses = CompletableFutures.successfulAsList(pings, CompletableFuture<List<ServerPing>> pingResponses = CompletableFutures.successfulAsList(pings,
(ex) -> fallback); (ex) -> fallback);
return switch (mode) {
case ALL -> pingResponses.thenApply(responses -> {
// Find the first non-fallback
for (ServerPing response : responses) {
if (response == fallback) {
continue;
}
if (response.getDescriptionComponent() == null) { // Return early if ping passthrough is not enabled
return response.asBuilder() if (!mode.enabled()) {
.description(Component.empty()) return CompletableFuture.completedFuture(fallback);
.build(); }
}
return response; return pingResponses.thenApply(responses -> {
// Find the first non-fallback
for (ServerPing response : responses) {
if (response == fallback) {
continue;
} }
return fallback;
});
case MODS -> pingResponses.thenApply(responses -> {
// Find the first non-fallback that contains a mod list
for (ServerPing response : responses) {
if (response == fallback) {
continue;
}
Optional<ModInfo> modInfo = response.getModinfo();
if (modInfo.isPresent()) {
return fallback.asBuilder().mods(modInfo.get()).build();
}
}
return fallback;
});
case DESCRIPTION -> pingResponses.thenApply(responses -> {
// Find the first non-fallback. If it includes a modlist, add it too.
for (ServerPing response : responses) {
if (response == fallback) {
continue;
}
if (response.getDescriptionComponent() == null) {
continue;
}
return new ServerPing( ServerPing.Version version;
fallback.getVersion(), if (mode.version()) {
fallback.getPlayers().orElse(null), version = response.getVersion();
response.getDescriptionComponent(), } else {
fallback.getFavicon().orElse(null), version = fallback.getVersion();
response.getModinfo().orElse(null)
);
} }
return fallback;
}); ServerPing.Players players;
// Not possible, but covered for completeness. if (mode.players()) {
default -> CompletableFuture.completedFuture(fallback); players = response.getPlayers().orElse(null);
}; } else {
players = fallback.getPlayers().orElse(null);
}
Component description;
if (mode.description()) {
if (response.getDescriptionComponent() != null) {
description = response.getDescriptionComponent();
} else {
description = Component.empty();
}
} else {
description = fallback.getDescriptionComponent();
}
Favicon favicon;
if (mode.favicon()) {
favicon = response.getFavicon().orElse(null);
} else {
favicon = fallback.getFavicon().orElse(null);
}
ModInfo modinfo;
if (mode.modinfo()) {
modinfo = response.getModinfo().orElse(null);
} else {
modinfo = fallback.getModinfo().orElse(null);
}
return new ServerPing(
version,
players,
description,
favicon,
modinfo
);
}
return fallback;
});
} }
/** /**
@@ -168,7 +177,7 @@ public class ServerListPingHandler {
? connection.getProtocolVersion() : ProtocolVersion.MAXIMUM_VERSION; ? connection.getProtocolVersion() : ProtocolVersion.MAXIMUM_VERSION;
PingPassthroughMode passthroughMode = configuration.getPingPassthrough(); PingPassthroughMode passthroughMode = configuration.getPingPassthrough();
if (passthroughMode == PingPassthroughMode.DISABLED) { if (!passthroughMode.enabled()) {
return CompletableFuture.completedFuture(constructLocalPing(shownVersion)); return CompletableFuture.completedFuture(constructLocalPing(shownVersion));
} else { } else {
String virtualHostStr = connection.getVirtualHost().map(InetSocketAddress::getHostString) String virtualHostStr = connection.getVirtualHost().map(InetSocketAddress::getHostString)
+14 -17
View File
@@ -1,5 +1,5 @@
# Config version. Do not change this # Config version. Do not change this
config-version = "2.8" config-version = "2.9"
# What port should the proxy be bound to? By default, we'll bind to all addresses on port 25565. # What port should the proxy be bound to? By default, we'll bind to all addresses on port 25565.
bind = "0.0.0.0:25565" bind = "0.0.0.0:25565"
@@ -43,7 +43,7 @@ forwarding-secret-file = "forwarding.secret"
# Announce whether or not your server supports Forge. If you run a modded server, we # Announce whether or not your server supports Forge. If you run a modded server, we
# suggest turning this on. # suggest turning this on.
# #
# If your network runs one modpack consistently, consider using ping-passthrough = "mods" # If your network runs one modpack consistently, consider using ping-passthrough.modinfo = true
# instead for a nicer display in the server list. # instead for a nicer display in the server list.
announce-forge = false announce-forge = false
@@ -51,21 +51,6 @@ announce-forge = false
# any existing player who is online if a duplicate connection attempt is made. # any existing player who is online if a duplicate connection attempt is made.
kick-existing-players = false kick-existing-players = false
# Should Velocity pass server list ping requests to a backend server?
# Available options:
# - "disabled": No pass-through will be done. The velocity.toml and server-icon.png
# will determine the initial server list ping response.
# - "mods": Passes only the mod list from your backend server into the response.
# The first server in your try list (or forced host) with a mod list will be
# used. If no backend servers can be contacted, Velocity won't display any
# mod information.
# - "description": Uses the description and mod list from the backend server. The first
# server in the try (or forced host) list that responds is used for the
# description and mod list.
# - "all": Uses the backend server's response as the proxy response. The Velocity
# configuration is used if no servers could be contacted.
ping-passthrough = "DISABLED"
# If enabled (default is false), then a sample of the online players on the proxy will be visible # If enabled (default is false), then a sample of the online players on the proxy will be visible
# when hovering over the player count in the server list. # when hovering over the player count in the server list.
# This doesn't have any effect when ping passthrough is set to either "description" or "all". # This doesn't have any effect when ping passthrough is set to either "description" or "all".
@@ -74,6 +59,18 @@ sample-players-in-ping = false
# If not enabled (default is true) player IP addresses will be replaced by <ip address withheld> in logs # If not enabled (default is true) player IP addresses will be replaced by <ip address withheld> in logs
enable-player-address-logging = true enable-player-address-logging = true
[ping-passthrough]
# Should Velocity pass the version number from the backend server when responding to server list ping requests?
version = false
# Should Velocity pass the player count from the backend server when responding to server list ping requests?
players = false
# Should Velocity pass the description from the backend server when responding to server list ping requests?
description = false
# Should Velocity pass the favicon (also known as the server icon) from the backend server when responding to server list ping requests?
favicon = false
# Should Velocity pass the mod list from the backend server when responding to server list ping requests?
modinfo = false
[packet-limiter] [packet-limiter]
# Size of the moving time window in seconds used to calculate average rates. # Size of the moving time window in seconds used to calculate average rates.
# A larger window tolerates short bursts while still enforcing the configured limits over time. # A larger window tolerates short bursts while still enforcing the configured limits over time.