Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
Comment thread
TheMiningTeamYT marked this conversation as resolved.
Outdated
*
* 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 {
Comment thread
WouterGritter marked this conversation as resolved.
Outdated
DISABLED,
MODS,
DESCRIPTION,
ALL
}
Comment thread
TheMiningTeamYT marked this conversation as resolved.
Outdated
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 {
Comment thread
WouterGritter marked this conversation as resolved.
Outdated
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 @@ -32,6 +32,7 @@
import com.velocitypowered.proxy.config.migration.MiniMessageTranslationsMigration;
import com.velocitypowered.proxy.config.migration.MotdMigration;
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.util.AddressUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
Expand Down Expand Up @@ -79,7 +80,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);

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.

= PingPassthroughMode.DEFAULT

was the primary change I suggested rather than having this randomly created instance;

the blow block of getOrElse() can also use the default instance, that way it's all defined in a singular place

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah okay I understand now. I honestly forgot that was there. That being said, I think change this to function more like the PacketLimiterConfig. I like how it puts all the code for parsing the sections in one place. Unless you disagree.

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.

The limiter config was just a quick toss together for the sake of getting this out, if I wanted everything in one place I would just have PingPassthroughMode.of(CommentedConfig) for that

@TheMiningTeamYT TheMiningTeamYT Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The limiter config was just a quick toss together for the sake of getting this out

Let me ask then, is there a different way you would prefer me to do this?
(Sorry if I come of as a bit annoying, I'm not the most experienced Java developer and I really wanna make sure I do this right so this PR can finally be merged).

@Expose
private boolean samplePlayersInPing = false;
private final Servers servers;
Expand Down Expand Up @@ -513,7 +514,8 @@ public static VelocityConfiguration read(Path path) throws IOException {
new MotdMigration(),
new MiniMessageTranslationsMigration(),
new TransferIntegrationMigration(),
new PacketLimiterMigration()
new PacketLimiterMigration(),
new PingPassthroughMigration(),
};

for (final ConfigurationMigration migration : migrations) {
Expand Down Expand Up @@ -555,9 +557,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));
Comment thread
TheMiningTeamYT marked this conversation as resolved.
Outdated
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 @@ -590,7 +595,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 @@ -30,7 +30,8 @@ public sealed interface ConfigurationMigration
MotdMigration,
MiniMessageTranslationsMigration,
TransferIntegrationMigration,
PacketLimiterMigration {
PacketLimiterMigration,
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
Comment thread
TheMiningTeamYT marked this conversation as resolved.
Outdated
*
* 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.9;
}

@Override
public void migrate(final CommentedFileConfig config, final Logger logger) {
Comment thread
TheMiningTeamYT marked this conversation as resolved.
// Get legacy ping passthrough value
final LegacyPingPassthroughMode legacyMode = config.getEnumOrElse("ping-passthrough",
LegacyPingPassthroughMode.DISABLED);
Comment thread
WouterGritter marked this conversation as resolved.
Outdated

config.removeComment("ping-passthrough");
config.remove("ping-passthrough");
Comment thread
WouterGritter marked this conversation as resolved.

// 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
Comment thread
WouterGritter marked this conversation as resolved.
Outdated
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.9");
}
}
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 @@ -100,60 +101,64 @@ private CompletableFuture<ServerPing> attemptPingPassthrough(VelocityInboundConn

CompletableFuture<List<ServerPing>> pingResponses = CompletableFutures.successfulAsList(pings,
(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 response.asBuilder()
.description(Component.empty())
.build();
}

return response;

// 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();
}

ServerPing.Players players;
if (mode.players) {
players = response.getPlayers().orElse(null);
} else {
players = fallback.getPlayers().orElse(null);
}
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();
}

Component description;
if (mode.description && response.getDescriptionComponent() != null) {
description = response.getDescriptionComponent();
} else {
description = fallback.getDescriptionComponent();
}
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(
fallback.getVersion(),
fallback.getPlayers().orElse(null),
response.getDescriptionComponent(),
fallback.getFavicon().orElse(null),
response.getModinfo().orElse(null)
);

Favicon favicon;
if (mode.favicon) {
favicon = response.getFavicon().orElse(null);
} else {
favicon = fallback.getFavicon().orElse(null);
}
return fallback;
});
// Not possible, but covered for completeness.
default -> CompletableFuture.completedFuture(fallback);
};

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 @@ -168,7 +173,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