diff --git a/api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java b/api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java index 12d3dbd106..c9db887523 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import net.kyori.adventure.text.Component; /** * Exposes certain proxy configuration information that plugins may use. @@ -47,11 +48,11 @@ public interface ProxyConfig { boolean shouldQueryShowPlugins(); /** - * Get the MOTD component shown in the tab list. + * Get the MOTD component shown in the tab list and the server list ping response. * * @return the motd component */ - net.kyori.adventure.text.Component getMotd(); + Component getMotd(); /** * Get the maximum players shown in the tab list. diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java index 2c7826e019..eca902e825 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -31,6 +31,7 @@ import com.velocitypowered.proxy.config.migration.KeyAuthenticationMigration; import com.velocitypowered.proxy.config.migration.MiniMessageTranslationsMigration; import com.velocitypowered.proxy.config.migration.MotdMigration; +import com.velocitypowered.proxy.config.migration.MotdMultilineMigration; import com.velocitypowered.proxy.config.migration.PacketLimiterMigration; import com.velocitypowered.proxy.config.migration.TransferIntegrationMigration; import com.velocitypowered.proxy.util.AddressUtil; @@ -48,6 +49,7 @@ import java.util.Map; import java.util.Optional; import java.util.Random; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -64,7 +66,7 @@ public class VelocityConfiguration implements ProxyConfig { @Expose private String bind = "0.0.0.0:25565"; @Expose - private String motd = "A Velocity Server"; + private List motd = ImmutableList.of("A Velocity Server"); @Expose private int showMaxPlayers = 500; @Expose @@ -91,7 +93,7 @@ public class VelocityConfiguration implements ProxyConfig { private final Metrics metrics; @Expose private boolean enablePlayerAddressLogging = true; - private net.kyori.adventure.text.@MonotonicNonNull Component motdAsComponent; + private @MonotonicNonNull Component motdAsComponent; private @Nullable Favicon favicon; @Expose private boolean forceKeyAuthentication = true; // Added in 1.19 @@ -107,7 +109,7 @@ private VelocityConfiguration(Servers servers, ForcedHosts forcedHosts, Advanced this.metrics = metrics; } - private VelocityConfiguration(String bind, String motd, int showMaxPlayers, boolean onlineMode, + private VelocityConfiguration(String bind, List motd, int showMaxPlayers, boolean onlineMode, boolean preventClientProxyConnections, boolean announceForge, PlayerInfoForwarding playerInfoForwardingMode, byte[] forwardingSecret, boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough, @@ -282,9 +284,12 @@ public boolean shouldQueryShowPlugins() { } @Override - public net.kyori.adventure.text.Component getMotd() { + public Component getMotd() { if (motdAsComponent == null) { - motdAsComponent = MiniMessage.miniMessage().deserialize(motd); + motdAsComponent = motd.stream() + .map(MiniMessage.miniMessage()::deserialize) + .reduce((a, b) -> a.appendNewline().append(b)) + .orElseGet(Component::empty); } return motdAsComponent; } @@ -513,7 +518,8 @@ public static VelocityConfiguration read(Path path) throws IOException { new MotdMigration(), new MiniMessageTranslationsMigration(), new TransferIntegrationMigration(), - new PacketLimiterMigration() + new PacketLimiterMigration(), + new MotdMultilineMigration() }; for (final ConfigurationMigration migration : migrations) { @@ -545,7 +551,7 @@ public static VelocityConfiguration read(Path path) throws IOException { } } final byte[] forwardingSecret = forwardingSecretString.getBytes(StandardCharsets.UTF_8); - final String motd = config.getOrElse("motd", "<#09add3>A Velocity Server"); + final List motd = config.getOrElse("motd", ImmutableList.of("<#09add3>A Velocity Server")); // Read the rest of the config final CommentedConfig serversConfig = config.get("servers"); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/migration/ConfigurationMigration.java b/proxy/src/main/java/com/velocitypowered/proxy/config/migration/ConfigurationMigration.java index 7c00b7bbb6..615c3c90ff 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/migration/ConfigurationMigration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/migration/ConfigurationMigration.java @@ -30,7 +30,8 @@ public sealed interface ConfigurationMigration MotdMigration, MiniMessageTranslationsMigration, TransferIntegrationMigration, - PacketLimiterMigration { + PacketLimiterMigration, + MotdMultilineMigration { boolean shouldMigrate(CommentedFileConfig config); void migrate(CommentedFileConfig config, Logger logger) throws IOException; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/migration/MotdMultilineMigration.java b/proxy/src/main/java/com/velocitypowered/proxy/config/migration/MotdMultilineMigration.java new file mode 100644 index 0000000000..c6e9ca70e4 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/migration/MotdMultilineMigration.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 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 . + */ + +package com.velocitypowered.proxy.config.migration; + +import com.electronwill.nightconfig.core.file.CommentedFileConfig; +import java.util.List; +import org.apache.logging.log4j.Logger; + +/** + * Migrates a single string motd to a multiline (string list) motd. + */ +public final class MotdMultilineMigration implements ConfigurationMigration { + + @Override + public boolean shouldMigrate(CommentedFileConfig config) { + return configVersion(config) < 2.9; + } + + @Override + public void migrate(CommentedFileConfig config, Logger logger) { + String motd = config.get("motd"); + config.set("motd", List.of(motd)); + + config.set("config-version", "2.9"); + } +} diff --git a/proxy/src/main/resources/default-velocity.toml b/proxy/src/main/resources/default-velocity.toml index 0eae2734a3..d8b52b0018 100644 --- a/proxy/src/main/resources/default-velocity.toml +++ b/proxy/src/main/resources/default-velocity.toml @@ -1,12 +1,14 @@ # 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. bind = "0.0.0.0:25565" # What should be the MOTD? This gets displayed when the player adds your server to # their server list. Only MiniMessage format is accepted. -motd = "<#09add3>A Velocity Server" +motd = [ + "<#09add3>A Velocity Server" +] # What should we display for the maximum number of players? (Velocity does not support a cap # on the number of players online.)