Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e2b8214
Added 'ALLBUTVERSION' option for ping passthrough.
Apr 15, 2024
38238b1
Trying to get the GitHub build action to run
Apr 16, 2024
f68bfb1
Added more configuration options for ping passthrough.
Apr 16, 2024
6dfdd9d
Updated default velocity.toml
Apr 16, 2024
1ac07b8
Update proxy/src/main/java/com/velocitypowered/proxy/connection/util/…
TheMiningTeamYT Apr 16, 2024
00652b5
Update proxy/src/main/java/com/velocitypowered/proxy/connection/util/…
TheMiningTeamYT Apr 16, 2024
73813fb
Update proxy/src/main/java/com/velocitypowered/proxy/connection/util/…
TheMiningTeamYT Apr 16, 2024
39f5c21
Update proxy/src/main/java/com/velocitypowered/proxy/connection/util/…
TheMiningTeamYT Apr 16, 2024
aca2132
Update proxy/src/main/java/com/velocitypowered/proxy/connection/util/…
TheMiningTeamYT Apr 16, 2024
72ed9c8
Update proxy/src/main/java/com/velocitypowered/proxy/connection/util/…
TheMiningTeamYT Apr 16, 2024
71c11a8
Add support for the legacy ping passthrough.
TheMiningTeamYT Apr 11, 2025
5eb3906
Merge branch 'dev/3.0.0' into ping-passthrough-dev
TheMiningTeamYT Apr 11, 2025
e76b3ed
Add more options for ping passthrough configuration. (Merge ping-pass…
TheMiningTeamYT Apr 11, 2025
8b53943
Merge branch 'PaperMC:dev/3.0.0' into dev/3.0.0
TheMiningTeamYT Apr 16, 2025
52d0d63
Merge branch 'dev/3.0.0' into ping-passthrough-dev
ButterDebugger Aug 11, 2025
fd30e8d
Removed legacy ping passthrough & added config migration
ButterDebugger Aug 11, 2025
f48e6fa
Cleaned up code to match code style
ButterDebugger Aug 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2018-2023 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;

/**
* Legacy modes for ping passthrough.
*/
public enum LegacyPingPassthroughMode {
DISABLED,
MODS,
DESCRIPTION,
ALL
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,36 @@
package com.velocitypowered.proxy.config;

/**
* Supported passthrough modes for ping passthrough.
* Object to contain all the things that can be toggled for ping passthrough.
*/
public enum PingPassthroughMode {
DISABLED,
MODS,
DESCRIPTION,
ALL
public class PingPassthroughMode {
public boolean version;
public boolean players;
public boolean description;
public boolean favicon;
public boolean modinfo;

/**
* Passthrough mode constructor.
* Looking at other code, I'm not sure the constructor is supposed to need a javadoc style comment,
* but checkstyle was yelling at me because I didn't include one.
* Probably for the best.
*
* @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 PingPassthroughMode(boolean version, boolean players, boolean description, boolean favicon, boolean modinfo) {
this.version = version;
this.players = players;
this.description = description;
this.favicon = favicon;
this.modinfo = modinfo;
}

public boolean enabled() {
return this.version || this.players || this.description || this.favicon || this.modinfo;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.velocitypowered.proxy.config.migration.ForwardingMigration;
import com.velocitypowered.proxy.config.migration.KeyAuthenticationMigration;
import com.velocitypowered.proxy.config.migration.MotdMigration;
import com.velocitypowered.proxy.config.migration.PingPassthroughMigration;
import com.velocitypowered.proxy.config.migration.TransferIntegrationMigration;
import com.velocitypowered.proxy.util.AddressUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
Expand Down Expand Up @@ -77,7 +78,7 @@ public class VelocityConfiguration implements ProxyConfig {
@Expose
private boolean onlineModeKickExistingPlayers = false;
@Expose
private PingPassthroughMode pingPassthrough = PingPassthroughMode.DISABLED;
private PingPassthroughMode pingPassthrough = new PingPassthroughMode(false, false, false, false, false);
@Expose
private boolean samplePlayersInPing = false;
private final Servers servers;
Expand Down Expand Up @@ -108,8 +109,7 @@ private VelocityConfiguration(String bind, String motd, int showMaxPlayers, bool
PlayerInfoForwarding playerInfoForwardingMode, byte[] forwardingSecret,
boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough,
boolean samplePlayersInPing, boolean enablePlayerAddressLogging, Servers servers,
ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics,
boolean forceKeyAuthentication) {
ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics, boolean forceKeyAuthentication) {
this.bind = bind;
this.motd = motd;
this.showMaxPlayers = showMaxPlayers;
Expand Down Expand Up @@ -504,7 +504,8 @@ public static VelocityConfiguration read(Path path) throws IOException {
new ForwardingMigration(),
new KeyAuthenticationMigration(),
new MotdMigration(),
new TransferIntegrationMigration()
new TransferIntegrationMigration(),
new PingPassthroughMigration()
};

for (final ConfigurationMigration migration : migrations) {
Expand Down Expand Up @@ -542,9 +543,12 @@ public static VelocityConfiguration read(Path path) throws IOException {
final CommentedConfig metricsConfig = config.get("metrics");
final PlayerInfoForwarding forwardingMode = config.getEnumOrElse(
"player-info-forwarding-mode", PlayerInfoForwarding.NONE);
final PingPassthroughMode pingPassthroughMode = config.getEnumOrElse("ping-passthrough",
PingPassthroughMode.DISABLED);

final PingPassthroughMode pingPassthrough = new PingPassthroughMode(
config.getOrElse("ping-passthrough-version", false),
config.getOrElse("ping-passthrough-players", false),
config.getOrElse("ping-passthrough-description", false),
config.getOrElse("ping-passthrough-favicon", false),
config.getOrElse("ping-passthrough-modinfo", false));
final boolean samplePlayersInPing = config.getOrElse("sample-players-in-ping", false);

final String bind = config.getOrElse("bind", "0.0.0.0:25565");
Expand Down Expand Up @@ -576,7 +580,7 @@ public static VelocityConfiguration read(Path path) throws IOException {
forwardingMode,
forwardingSecret,
kickExisting,
pingPassthroughMode,
pingPassthrough,
samplePlayersInPing,
enablePlayerAddressLogging,
new Servers(serversConfig),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public sealed interface ConfigurationMigration
permits ForwardingMigration,
KeyAuthenticationMigration,
MotdMigration,
TransferIntegrationMigration {
TransferIntegrationMigration,
PingPassthroughMigration {
boolean shouldMigrate(CommentedFileConfig config);

void migrate(CommentedFileConfig config, Logger logger) throws IOException;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2024 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 com.velocitypowered.proxy.config.LegacyPingPassthroughMode;
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.8;
}

@Override
public void migrate(final CommentedFileConfig config, final Logger logger) {
// Get legacy ping passthrough value
final LegacyPingPassthroughMode legacyMode = config.getEnumOrElse("ping-passthrough",
LegacyPingPassthroughMode.DISABLED);

config.removeComment("ping-passthrough");
config.remove("ping-passthrough");

// Create ping passthrough entry for the version
config.set("ping-passthrough-version", legacyMode.equals(LegacyPingPassthroughMode.ALL));
config.setComment(
"ping-passthrough-version",
" Should Velocity pass the version number from the backend server when responding to server list ping requests?"
);

// Create ping passthrough entry for the players
config.set("ping-passthrough-players", legacyMode.equals(LegacyPingPassthroughMode.ALL));
config.setComment(
"ping-passthrough-players",
" Should Velocity pass the player count from the backend server when responding to server list ping requests?"
);

// Create ping passthrough entry for the description
config.set("ping-passthrough-description",
legacyMode.equals(LegacyPingPassthroughMode.ALL)
|| legacyMode.equals(LegacyPingPassthroughMode.DESCRIPTION)
);
config.setComment(
"ping-passthrough-description",
" Should Velocity pass the description from the backend server when responding to server list ping requests?"
);

// Create ping passthrough entry for the favicon
config.set("ping-passthrough-favicon", legacyMode.equals(LegacyPingPassthroughMode.ALL));
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?"
);

// Create ping passthrough entry for the mods info
config.set("ping-passthrough-modinfo",
legacyMode.equals(LegacyPingPassthroughMode.ALL)
|| legacyMode.equals(LegacyPingPassthroughMode.MODS)
|| legacyMode.equals(LegacyPingPassthroughMode.DESCRIPTION)
);
config.setComment(
"ping-passthrough-modinfo",
" Should Velocity pass the mod list from the backend server when responding to server list ping requests?"
);

// Update config version
config.set("config-version", "2.8");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.velocitypowered.api.proxy.server.PingOptions;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.api.util.Favicon;
import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.config.PingPassthroughMode;
Expand Down Expand Up @@ -99,58 +100,64 @@ private CompletableFuture<ServerPing> attemptPingPassthrough(VelocityInboundConn

CompletableFuture<List<ServerPing>> pingResponses = CompletableFutures.successfulAsList(pings,
(ex) -> fallback);
switch (mode) {
case ALL:
return pingResponses.thenApply(responses -> {
// Find the first non-fallback
for (ServerPing response : responses) {
if (response == fallback) {
continue;
}
return response;
}
return fallback;
});
case MODS:
return 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:
return 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(
fallback.getVersion(),
fallback.getPlayers().orElse(null),
response.getDescriptionComponent(),
fallback.getFavicon().orElse(null),
response.getModinfo().orElse(null)
);
}
return fallback;
});
// Not possible, but covered for completeness.
default:
return CompletableFuture.completedFuture(fallback);

// Return early if ping passthrough is not enabled
if (!mode.enabled()) {
return CompletableFuture.completedFuture(fallback);
}

return pingResponses.thenApply(responses -> {
// Find the first non-fallback
for (ServerPing response : responses) {
if (response == fallback) {
continue;
}

ServerPing.Version version;
if (mode.version) {
version = response.getVersion();
} else {
version = fallback.getVersion();
}
Comment on lines +117 to +121
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ternaries for each of those would make them a bit more compact. And not sure if this means much, but this looks functionally different to the description and mods search from before where it would continue iterating responses if they're null in the current one

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the ternaries, personal preference but that's an easy fix to make down the road. I don't think that should keep you from merging the pull request.
As for the difference in handling mod/description search: If you were to implement that in this code, you'd run the risk of, say, returning the player count from one server and the mod list from another, or returning the player count & mod list from a different server from the one the player is about to join, so unless someone jumps in here and says "hey I need that," I think it's fine the way it is.


ServerPing.Players players;
if (mode.players) {
players = response.getPlayers().orElse(null);
} else {
players = fallback.getPlayers().orElse(null);
}

net.kyori.adventure.text.Component description;
if (mode.description && response.getDescriptionComponent() != null) {
description = response.getDescriptionComponent();
} 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;
});
}

/**
Expand All @@ -165,7 +172,7 @@ public CompletableFuture<ServerPing> getInitialPing(VelocityInboundConnection co
? connection.getProtocolVersion() : ProtocolVersion.MAXIMUM_VERSION;
PingPassthroughMode passthroughMode = configuration.getPingPassthrough();

if (passthroughMode == PingPassthroughMode.DISABLED) {
if (!passthroughMode.enabled()) {
return CompletableFuture.completedFuture(constructLocalPing(shownVersion));
} else {
String virtualHostStr = connection.getVirtualHost().map(InetSocketAddress::getHostString)
Expand Down
Loading