mirror of
https://github.com/PaperMC/Velocity.git
synced 2026-02-17 14:37:43 +01:00
Seperate out night-config toml loading logic
This commit is contained in:
@@ -45,6 +45,7 @@ import com.velocitypowered.proxy.command.builtin.SendCommand;
|
|||||||
import com.velocitypowered.proxy.command.builtin.ServerCommand;
|
import com.velocitypowered.proxy.command.builtin.ServerCommand;
|
||||||
import com.velocitypowered.proxy.command.builtin.ShutdownCommand;
|
import com.velocitypowered.proxy.command.builtin.ShutdownCommand;
|
||||||
import com.velocitypowered.proxy.command.builtin.VelocityCommand;
|
import com.velocitypowered.proxy.command.builtin.VelocityCommand;
|
||||||
|
import com.velocitypowered.proxy.config.LegacyConfigurationLoader;
|
||||||
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
|
import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
|
||||||
@@ -383,7 +384,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
private void doStartupConfigLoad() {
|
private void doStartupConfigLoad() {
|
||||||
try {
|
try {
|
||||||
Path configPath = Path.of("velocity.toml");
|
Path configPath = Path.of("velocity.toml");
|
||||||
configuration = VelocityConfiguration.read(configPath);
|
configuration = LegacyConfigurationLoader.read(configPath);
|
||||||
|
|
||||||
if (!configuration.validate()) {
|
if (!configuration.validate()) {
|
||||||
logger.error("Your configuration is invalid. Velocity will not start up until the errors "
|
logger.error("Your configuration is invalid. Velocity will not start up until the errors "
|
||||||
@@ -461,7 +462,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
*/
|
*/
|
||||||
public boolean reloadConfiguration() throws IOException {
|
public boolean reloadConfiguration() throws IOException {
|
||||||
Path configPath = Path.of("velocity.toml");
|
Path configPath = Path.of("velocity.toml");
|
||||||
VelocityConfiguration newConfiguration = VelocityConfiguration.read(configPath);
|
VelocityConfiguration newConfiguration = LegacyConfigurationLoader.read(configPath);
|
||||||
|
|
||||||
if (!newConfiguration.validate()) {
|
if (!newConfiguration.validate()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Velocity Configurate Loader entry utils.
|
||||||
|
*/
|
||||||
|
public class ConfigurationLoader {
|
||||||
|
|
||||||
|
private ConfigurationLoader() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* performs legacy configuration migration if needed.
|
||||||
|
*
|
||||||
|
* @return {@code true} if a migration was performed, {@code false} otherwise
|
||||||
|
*/
|
||||||
|
public static boolean migrateIfNeeded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* loads the velocity configuration.
|
||||||
|
*
|
||||||
|
* @return the loaded configuration
|
||||||
|
*/
|
||||||
|
public static VelocityConfiguration loadConfiguration() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import static com.velocitypowered.proxy.config.VelocityConfiguration.Servers.cleanServerName;
|
||||||
|
import static com.velocitypowered.proxy.config.VelocityConfiguration.generateRandomString;
|
||||||
|
|
||||||
|
import com.electronwill.nightconfig.core.CommentedConfig;
|
||||||
|
import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
||||||
|
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.velocitypowered.proxy.config.migration.ConfigurationMigration;
|
||||||
|
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.TransferIntegrationMigration;
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy configuration loader for Velocity.
|
||||||
|
*/
|
||||||
|
public class LegacyConfigurationLoader {
|
||||||
|
private static final Logger logger = LogManager.getLogger(LegacyConfigurationLoader.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the Velocity configuration from {@code path}.
|
||||||
|
*
|
||||||
|
* @param path the path to read from
|
||||||
|
* @return the deserialized Velocity configuration
|
||||||
|
* @throws IOException if we could not read from the {@code path}.
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE",
|
||||||
|
justification = "I looked carefully and there's no way SpotBugs is right.")
|
||||||
|
public static VelocityConfiguration read(Path path) throws IOException {
|
||||||
|
URL defaultConfigLocation = VelocityConfiguration.class.getClassLoader()
|
||||||
|
.getResource("default-velocity.toml");
|
||||||
|
if (defaultConfigLocation == null) {
|
||||||
|
throw new RuntimeException("Default configuration file does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the forwarding-secret file on first-time startup if it doesn't exist
|
||||||
|
final Path defaultForwardingSecretPath = Path.of("forwarding.secret");
|
||||||
|
if (Files.notExists(path) && Files.notExists(defaultForwardingSecretPath)) {
|
||||||
|
Files.writeString(defaultForwardingSecretPath, generateRandomString(12));
|
||||||
|
}
|
||||||
|
|
||||||
|
try (final CommentedFileConfig config = CommentedFileConfig.builder(path)
|
||||||
|
.defaultData(defaultConfigLocation)
|
||||||
|
.autosave()
|
||||||
|
.preserveInsertionOrder()
|
||||||
|
.sync()
|
||||||
|
.build()
|
||||||
|
) {
|
||||||
|
config.load();
|
||||||
|
|
||||||
|
final ConfigurationMigration[] migrations = {
|
||||||
|
new ForwardingMigration(),
|
||||||
|
new KeyAuthenticationMigration(),
|
||||||
|
new MotdMigration(),
|
||||||
|
new TransferIntegrationMigration()
|
||||||
|
};
|
||||||
|
|
||||||
|
for (final ConfigurationMigration migration : migrations) {
|
||||||
|
if (migration.shouldMigrate(config)) {
|
||||||
|
migration.migrate(config, logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String forwardingSecretString = System.getenv().getOrDefault(
|
||||||
|
"VELOCITY_FORWARDING_SECRET", "");
|
||||||
|
if (forwardingSecretString.isEmpty()) {
|
||||||
|
final String forwardSecretFile = config.get("forwarding-secret-file");
|
||||||
|
final Path secretPath = forwardSecretFile == null
|
||||||
|
? defaultForwardingSecretPath
|
||||||
|
: Path.of(forwardSecretFile);
|
||||||
|
if (Files.exists(secretPath)) {
|
||||||
|
if (Files.isRegularFile(secretPath)) {
|
||||||
|
forwardingSecretString = String.join("", Files.readAllLines(secretPath));
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"The file " + forwardSecretFile + " is not a valid file or it is a directory.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("The forwarding-secret-file does not exist.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final byte[] forwardingSecret = forwardingSecretString.getBytes(StandardCharsets.UTF_8);
|
||||||
|
final String motd = config.getOrElse("motd", "<#09add3>A Velocity Server");
|
||||||
|
|
||||||
|
// Read the rest of the config
|
||||||
|
final CommentedConfig serversConfig = config.get("servers");
|
||||||
|
final CommentedConfig forcedHostsConfig = config.get("forced-hosts");
|
||||||
|
final CommentedConfig advancedConfig = config.get("advanced");
|
||||||
|
final CommentedConfig queryConfig = config.get("query");
|
||||||
|
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 String bind = config.getOrElse("bind", "0.0.0.0:25565");
|
||||||
|
final int maxPlayers = config.getIntOrElse("show-max-players", 500);
|
||||||
|
final boolean onlineMode = config.getOrElse("online-mode", true);
|
||||||
|
final boolean forceKeyAuthentication = config.getOrElse("force-key-authentication", true);
|
||||||
|
final boolean announceForge = config.getOrElse("announce-forge", true);
|
||||||
|
final boolean preventClientProxyConnections = config.getOrElse(
|
||||||
|
"prevent-client-proxy-connections", false);
|
||||||
|
final boolean kickExisting = config.getOrElse("kick-existing-players", false);
|
||||||
|
final boolean enablePlayerAddressLogging = config.getOrElse(
|
||||||
|
"enable-player-address-logging", true);
|
||||||
|
|
||||||
|
// Throw an exception if the forwarding-secret file is empty and the proxy is using a
|
||||||
|
// forwarding mode that requires it.
|
||||||
|
if (forwardingSecret.length == 0
|
||||||
|
&& (forwardingMode == PlayerInfoForwarding.MODERN
|
||||||
|
|| forwardingMode == PlayerInfoForwarding.BUNGEEGUARD)) {
|
||||||
|
throw new RuntimeException("The forwarding-secret file must not be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new VelocityConfiguration(
|
||||||
|
bind,
|
||||||
|
motd,
|
||||||
|
maxPlayers,
|
||||||
|
onlineMode,
|
||||||
|
preventClientProxyConnections,
|
||||||
|
announceForge,
|
||||||
|
forwardingMode,
|
||||||
|
forwardingSecret,
|
||||||
|
kickExisting,
|
||||||
|
pingPassthroughMode,
|
||||||
|
enablePlayerAddressLogging,
|
||||||
|
readServers(serversConfig),
|
||||||
|
readForcedHosts(forcedHostsConfig),
|
||||||
|
readAdvanced(advancedConfig),
|
||||||
|
readQuery(queryConfig),
|
||||||
|
readMetrics(metricsConfig),
|
||||||
|
forceKeyAuthentication
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static VelocityConfiguration.Servers readServers(CommentedConfig config) {
|
||||||
|
if (config != null) {
|
||||||
|
Map<String, String> servers = new HashMap<>();
|
||||||
|
for (UnmodifiableConfig.Entry entry : config.entrySet()) {
|
||||||
|
if (entry.getValue() instanceof String) {
|
||||||
|
servers.put(cleanServerName(entry.getKey()), entry.getValue());
|
||||||
|
} else {
|
||||||
|
if (!entry.getKey().equalsIgnoreCase("try")) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Server entry " + entry.getKey() + " is not a string!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new VelocityConfiguration.Servers(ImmutableMap.copyOf(servers),
|
||||||
|
config.getOrElse("try", ImmutableList.of("lobby")));
|
||||||
|
}
|
||||||
|
return new VelocityConfiguration.Servers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VelocityConfiguration.ForcedHosts readForcedHosts(CommentedConfig config) {
|
||||||
|
if (config != null) {
|
||||||
|
Map<String, List<String>> forcedHosts = new HashMap<>();
|
||||||
|
for (UnmodifiableConfig.Entry entry : config.entrySet()) {
|
||||||
|
if (entry.getValue() instanceof String) {
|
||||||
|
forcedHosts.put(entry.getKey().toLowerCase(Locale.ROOT),
|
||||||
|
ImmutableList.of(entry.getValue()));
|
||||||
|
} else if (entry.getValue() instanceof List) {
|
||||||
|
forcedHosts.put(entry.getKey().toLowerCase(Locale.ROOT),
|
||||||
|
ImmutableList.copyOf((List<String>) entry.getValue()));
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Invalid value of type " + entry.getValue().getClass() + " in forced hosts!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new VelocityConfiguration.ForcedHosts(forcedHosts);
|
||||||
|
}
|
||||||
|
return new VelocityConfiguration.ForcedHosts();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VelocityConfiguration.Advanced readAdvanced(CommentedConfig config) {
|
||||||
|
if (config != null) {
|
||||||
|
int compressionThreshold = config.getIntOrElse("compression-threshold", 256);
|
||||||
|
int compressionLevel = config.getIntOrElse("compression-level", -1);
|
||||||
|
int loginRatelimit = config.getIntOrElse("login-ratelimit", 3000);
|
||||||
|
int connectionTimeout = config.getIntOrElse("connection-timeout", 5000);
|
||||||
|
int readTimeout = config.getIntOrElse("read-timeout", 30000);
|
||||||
|
boolean proxyProtocol = false;
|
||||||
|
if (config.contains("haproxy-protocol")) {
|
||||||
|
proxyProtocol = config.getOrElse("haproxy-protocol", false);
|
||||||
|
} else {
|
||||||
|
proxyProtocol = config.getOrElse("proxy-protocol", false);
|
||||||
|
}
|
||||||
|
boolean tcpFastOpen = config.getOrElse("tcp-fast-open", false);
|
||||||
|
boolean bungeePluginMessageChannel = config.getOrElse("bungee-plugin-message-channel", true);
|
||||||
|
boolean showPingRequests = config.getOrElse("show-ping-requests", false);
|
||||||
|
boolean failoverOnUnexpectedServerDisconnect = config
|
||||||
|
.getOrElse("failover-on-unexpected-server-disconnect", true);
|
||||||
|
boolean announceProxyCommands = config.getOrElse("announce-proxy-commands", true);
|
||||||
|
boolean logCommandExecutions = config.getOrElse("log-command-executions", false);
|
||||||
|
boolean logPlayerConnections = config.getOrElse("log-player-connections", true);
|
||||||
|
boolean acceptTransfers = config.getOrElse("accepts-transfers", false);
|
||||||
|
return new VelocityConfiguration.Advanced(compressionThreshold, compressionLevel,
|
||||||
|
loginRatelimit, connectionTimeout, readTimeout, proxyProtocol, tcpFastOpen,
|
||||||
|
bungeePluginMessageChannel, showPingRequests, failoverOnUnexpectedServerDisconnect,
|
||||||
|
announceProxyCommands, logCommandExecutions, logPlayerConnections, acceptTransfers);
|
||||||
|
}
|
||||||
|
return new VelocityConfiguration.Advanced();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VelocityConfiguration.Query readQuery(CommentedConfig config) {
|
||||||
|
if (config != null) {
|
||||||
|
boolean queryEnabled = config.getOrElse("enabled", false);
|
||||||
|
int queryPort = config.getIntOrElse("port", 25565);
|
||||||
|
String queryMap = config.getOrElse("map", "Velocity");
|
||||||
|
boolean showPlugins = config.getOrElse("show-plugins", false);
|
||||||
|
return new VelocityConfiguration.Query(queryEnabled, queryPort, queryMap, showPlugins);
|
||||||
|
}
|
||||||
|
return new VelocityConfiguration.Query();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VelocityConfiguration.Metrics readMetrics(CommentedConfig config) {
|
||||||
|
if (config != null) {
|
||||||
|
boolean enabled = config.getOrElse("enabled", true);
|
||||||
|
return new VelocityConfiguration.Metrics(enabled);
|
||||||
|
}
|
||||||
|
return new VelocityConfiguration.Metrics();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,32 +17,19 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.config;
|
package com.velocitypowered.proxy.config;
|
||||||
|
|
||||||
import com.electronwill.nightconfig.core.CommentedConfig;
|
|
||||||
import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
|
||||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.gson.annotations.Expose;
|
import com.google.gson.annotations.Expose;
|
||||||
import com.velocitypowered.api.proxy.config.ProxyConfig;
|
import com.velocitypowered.api.proxy.config.ProxyConfig;
|
||||||
import com.velocitypowered.api.util.Favicon;
|
import com.velocitypowered.api.util.Favicon;
|
||||||
import com.velocitypowered.proxy.config.migration.ConfigurationMigration;
|
|
||||||
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.TransferIntegrationMigration;
|
|
||||||
import com.velocitypowered.proxy.util.AddressUtil;
|
import com.velocitypowered.proxy.util.AddressUtil;
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@@ -51,10 +38,12 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Velocity's configuration.
|
* Velocity's configuration.
|
||||||
*/
|
*/
|
||||||
|
@ConfigSerializable
|
||||||
public class VelocityConfiguration implements ProxyConfig {
|
public class VelocityConfiguration implements ProxyConfig {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(VelocityConfiguration.class);
|
private static final Logger logger = LogManager.getLogger(VelocityConfiguration.class);
|
||||||
@@ -101,12 +90,14 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
this.metrics = metrics;
|
this.metrics = metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VelocityConfiguration(String bind, String motd, int showMaxPlayers, boolean onlineMode,
|
VelocityConfiguration(String bind, String motd, int showMaxPlayers, boolean onlineMode,
|
||||||
boolean preventClientProxyConnections, boolean announceForge,
|
boolean preventClientProxyConnections, boolean announceForge,
|
||||||
PlayerInfoForwarding playerInfoForwardingMode, byte[] forwardingSecret,
|
PlayerInfoForwarding playerInfoForwardingMode, byte[] forwardingSecret,
|
||||||
boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough,
|
boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough,
|
||||||
boolean enablePlayerAddressLogging, Servers servers, ForcedHosts forcedHosts,
|
boolean enablePlayerAddressLogging, Servers servers,
|
||||||
Advanced advanced, Query query, Metrics metrics, boolean forceKeyAuthentication) {
|
ForcedHosts forcedHosts,
|
||||||
|
Advanced advanced, Query query, Metrics metrics,
|
||||||
|
boolean forceKeyAuthentication) {
|
||||||
this.bind = bind;
|
this.bind = bind;
|
||||||
this.motd = motd;
|
this.motd = motd;
|
||||||
this.showMaxPlayers = showMaxPlayers;
|
this.showMaxPlayers = showMaxPlayers;
|
||||||
@@ -427,123 +418,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the Velocity configuration from {@code path}.
|
|
||||||
*
|
|
||||||
* @param path the path to read from
|
|
||||||
* @return the deserialized Velocity configuration
|
|
||||||
* @throws IOException if we could not read from the {@code path}.
|
|
||||||
*/
|
|
||||||
@SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE",
|
|
||||||
justification = "I looked carefully and there's no way SpotBugs is right.")
|
|
||||||
public static VelocityConfiguration read(Path path) throws IOException {
|
|
||||||
URL defaultConfigLocation = VelocityConfiguration.class.getClassLoader()
|
|
||||||
.getResource("default-velocity.toml");
|
|
||||||
if (defaultConfigLocation == null) {
|
|
||||||
throw new RuntimeException("Default configuration file does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the forwarding-secret file on first-time startup if it doesn't exist
|
|
||||||
final Path defaultForwardingSecretPath = Path.of("forwarding.secret");
|
|
||||||
if (Files.notExists(path) && Files.notExists(defaultForwardingSecretPath)) {
|
|
||||||
Files.writeString(defaultForwardingSecretPath, generateRandomString(12));
|
|
||||||
}
|
|
||||||
|
|
||||||
try (final CommentedFileConfig config = CommentedFileConfig.builder(path)
|
|
||||||
.defaultData(defaultConfigLocation)
|
|
||||||
.autosave()
|
|
||||||
.preserveInsertionOrder()
|
|
||||||
.sync()
|
|
||||||
.build()
|
|
||||||
) {
|
|
||||||
config.load();
|
|
||||||
|
|
||||||
final ConfigurationMigration[] migrations = {
|
|
||||||
new ForwardingMigration(),
|
|
||||||
new KeyAuthenticationMigration(),
|
|
||||||
new MotdMigration(),
|
|
||||||
new TransferIntegrationMigration()
|
|
||||||
};
|
|
||||||
|
|
||||||
for (final ConfigurationMigration migration : migrations) {
|
|
||||||
if (migration.shouldMigrate(config)) {
|
|
||||||
migration.migrate(config, logger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String forwardingSecretString = System.getenv().getOrDefault(
|
|
||||||
"VELOCITY_FORWARDING_SECRET", "");
|
|
||||||
if (forwardingSecretString.isEmpty()) {
|
|
||||||
final String forwardSecretFile = config.get("forwarding-secret-file");
|
|
||||||
final Path secretPath = forwardSecretFile == null
|
|
||||||
? defaultForwardingSecretPath
|
|
||||||
: Path.of(forwardSecretFile);
|
|
||||||
if (Files.exists(secretPath)) {
|
|
||||||
if (Files.isRegularFile(secretPath)) {
|
|
||||||
forwardingSecretString = String.join("", Files.readAllLines(secretPath));
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"The file " + forwardSecretFile + " is not a valid file or it is a directory.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("The forwarding-secret-file does not exist.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final byte[] forwardingSecret = forwardingSecretString.getBytes(StandardCharsets.UTF_8);
|
|
||||||
final String motd = config.getOrElse("motd", "<#09add3>A Velocity Server");
|
|
||||||
|
|
||||||
// Read the rest of the config
|
|
||||||
final CommentedConfig serversConfig = config.get("servers");
|
|
||||||
final CommentedConfig forcedHostsConfig = config.get("forced-hosts");
|
|
||||||
final CommentedConfig advancedConfig = config.get("advanced");
|
|
||||||
final CommentedConfig queryConfig = config.get("query");
|
|
||||||
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 String bind = config.getOrElse("bind", "0.0.0.0:25565");
|
|
||||||
final int maxPlayers = config.getIntOrElse("show-max-players", 500);
|
|
||||||
final boolean onlineMode = config.getOrElse("online-mode", true);
|
|
||||||
final boolean forceKeyAuthentication = config.getOrElse("force-key-authentication", true);
|
|
||||||
final boolean announceForge = config.getOrElse("announce-forge", true);
|
|
||||||
final boolean preventClientProxyConnections = config.getOrElse(
|
|
||||||
"prevent-client-proxy-connections", false);
|
|
||||||
final boolean kickExisting = config.getOrElse("kick-existing-players", false);
|
|
||||||
final boolean enablePlayerAddressLogging = config.getOrElse(
|
|
||||||
"enable-player-address-logging", true);
|
|
||||||
|
|
||||||
// Throw an exception if the forwarding-secret file is empty and the proxy is using a
|
|
||||||
// forwarding mode that requires it.
|
|
||||||
if (forwardingSecret.length == 0
|
|
||||||
&& (forwardingMode == PlayerInfoForwarding.MODERN
|
|
||||||
|| forwardingMode == PlayerInfoForwarding.BUNGEEGUARD)) {
|
|
||||||
throw new RuntimeException("The forwarding-secret file must not be empty.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new VelocityConfiguration(
|
|
||||||
bind,
|
|
||||||
motd,
|
|
||||||
maxPlayers,
|
|
||||||
onlineMode,
|
|
||||||
preventClientProxyConnections,
|
|
||||||
announceForge,
|
|
||||||
forwardingMode,
|
|
||||||
forwardingSecret,
|
|
||||||
kickExisting,
|
|
||||||
pingPassthroughMode,
|
|
||||||
enablePlayerAddressLogging,
|
|
||||||
new Servers(serversConfig),
|
|
||||||
new ForcedHosts(forcedHostsConfig),
|
|
||||||
new Advanced(advancedConfig),
|
|
||||||
new Query(queryConfig),
|
|
||||||
new Metrics(metricsConfig),
|
|
||||||
forceKeyAuthentication
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a Random String.
|
* Generates a Random String.
|
||||||
*
|
*
|
||||||
@@ -564,7 +438,7 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
return onlineModeKickExistingPlayers;
|
return onlineModeKickExistingPlayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Servers {
|
static class Servers {
|
||||||
|
|
||||||
private Map<String, String> servers = ImmutableMap.of(
|
private Map<String, String> servers = ImmutableMap.of(
|
||||||
"lobby", "127.0.0.1:30066",
|
"lobby", "127.0.0.1:30066",
|
||||||
@@ -573,28 +447,10 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
);
|
);
|
||||||
private List<String> attemptConnectionOrder = ImmutableList.of("lobby");
|
private List<String> attemptConnectionOrder = ImmutableList.of("lobby");
|
||||||
|
|
||||||
private Servers() {
|
Servers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Servers(CommentedConfig config) {
|
Servers(Map<String, String> servers, List<String> attemptConnectionOrder) {
|
||||||
if (config != null) {
|
|
||||||
Map<String, String> servers = new HashMap<>();
|
|
||||||
for (UnmodifiableConfig.Entry entry : config.entrySet()) {
|
|
||||||
if (entry.getValue() instanceof String) {
|
|
||||||
servers.put(cleanServerName(entry.getKey()), entry.getValue());
|
|
||||||
} else {
|
|
||||||
if (!entry.getKey().equalsIgnoreCase("try")) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Server entry " + entry.getKey() + " is not a string!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.servers = ImmutableMap.copyOf(servers);
|
|
||||||
this.attemptConnectionOrder = config.getOrElse("try", attemptConnectionOrder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Servers(Map<String, String> servers, List<String> attemptConnectionOrder) {
|
|
||||||
this.servers = servers;
|
this.servers = servers;
|
||||||
this.attemptConnectionOrder = attemptConnectionOrder;
|
this.attemptConnectionOrder = attemptConnectionOrder;
|
||||||
}
|
}
|
||||||
@@ -623,7 +479,7 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
* @param name the server name to clean
|
* @param name the server name to clean
|
||||||
* @return the cleaned server name
|
* @return the cleaned server name
|
||||||
*/
|
*/
|
||||||
private String cleanServerName(String name) {
|
static String cleanServerName(String name) {
|
||||||
return name.replace("\"", "");
|
return name.replace("\"", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,7 +492,7 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ForcedHosts {
|
static class ForcedHosts {
|
||||||
|
|
||||||
private Map<String, List<String>> forcedHosts = ImmutableMap.of(
|
private Map<String, List<String>> forcedHosts = ImmutableMap.of(
|
||||||
"lobby.example.com", ImmutableList.of("lobby"),
|
"lobby.example.com", ImmutableList.of("lobby"),
|
||||||
@@ -644,29 +500,10 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
"minigames.example.com", ImmutableList.of("minigames")
|
"minigames.example.com", ImmutableList.of("minigames")
|
||||||
);
|
);
|
||||||
|
|
||||||
private ForcedHosts() {
|
ForcedHosts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ForcedHosts(CommentedConfig config) {
|
ForcedHosts(Map<String, List<String>> forcedHosts) {
|
||||||
if (config != null) {
|
|
||||||
Map<String, List<String>> forcedHosts = new HashMap<>();
|
|
||||||
for (UnmodifiableConfig.Entry entry : config.entrySet()) {
|
|
||||||
if (entry.getValue() instanceof String) {
|
|
||||||
forcedHosts.put(entry.getKey().toLowerCase(Locale.ROOT),
|
|
||||||
ImmutableList.of(entry.getValue()));
|
|
||||||
} else if (entry.getValue() instanceof List) {
|
|
||||||
forcedHosts.put(entry.getKey().toLowerCase(Locale.ROOT),
|
|
||||||
ImmutableList.copyOf((List<String>) entry.getValue()));
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Invalid value of type " + entry.getValue().getClass() + " in forced hosts!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.forcedHosts = ImmutableMap.copyOf(forcedHosts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ForcedHosts(Map<String, List<String>> forcedHosts) {
|
|
||||||
this.forcedHosts = forcedHosts;
|
this.forcedHosts = forcedHosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,7 +523,7 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Advanced {
|
static class Advanced {
|
||||||
|
|
||||||
@Expose
|
@Expose
|
||||||
private int compressionThreshold = 256;
|
private int compressionThreshold = 256;
|
||||||
@@ -717,33 +554,31 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
@Expose
|
@Expose
|
||||||
private boolean acceptTransfers = false;
|
private boolean acceptTransfers = false;
|
||||||
|
|
||||||
private Advanced() {
|
Advanced() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Advanced(CommentedConfig config) {
|
Advanced(int compressionThreshold, int compressionLevel, int loginRatelimit,
|
||||||
if (config != null) {
|
int connectionTimeout, int readTimeout, boolean proxyProtocol, boolean tcpFastOpen,
|
||||||
this.compressionThreshold = config.getIntOrElse("compression-threshold", 256);
|
boolean bungeePluginMessageChannel, boolean showPingRequests,
|
||||||
this.compressionLevel = config.getIntOrElse("compression-level", -1);
|
boolean failoverOnUnexpectedServerDisconnect, boolean announceProxyCommands,
|
||||||
this.loginRatelimit = config.getIntOrElse("login-ratelimit", 3000);
|
boolean logCommandExecutions, boolean logPlayerConnections, boolean acceptTransfers) {
|
||||||
this.connectionTimeout = config.getIntOrElse("connection-timeout", 5000);
|
this.compressionThreshold = compressionThreshold;
|
||||||
this.readTimeout = config.getIntOrElse("read-timeout", 30000);
|
this.compressionLevel = compressionLevel;
|
||||||
if (config.contains("haproxy-protocol")) {
|
this.loginRatelimit = loginRatelimit;
|
||||||
this.proxyProtocol = config.getOrElse("haproxy-protocol", false);
|
this.connectionTimeout = connectionTimeout;
|
||||||
} else {
|
this.readTimeout = readTimeout;
|
||||||
this.proxyProtocol = config.getOrElse("proxy-protocol", false);
|
this.proxyProtocol = proxyProtocol;
|
||||||
}
|
this.tcpFastOpen = tcpFastOpen;
|
||||||
this.tcpFastOpen = config.getOrElse("tcp-fast-open", false);
|
this.bungeePluginMessageChannel = bungeePluginMessageChannel;
|
||||||
this.bungeePluginMessageChannel = config.getOrElse("bungee-plugin-message-channel", true);
|
this.showPingRequests = showPingRequests;
|
||||||
this.showPingRequests = config.getOrElse("show-ping-requests", false);
|
this.failoverOnUnexpectedServerDisconnect = failoverOnUnexpectedServerDisconnect;
|
||||||
this.failoverOnUnexpectedServerDisconnect = config
|
this.announceProxyCommands = announceProxyCommands;
|
||||||
.getOrElse("failover-on-unexpected-server-disconnect", true);
|
this.logCommandExecutions = logCommandExecutions;
|
||||||
this.announceProxyCommands = config.getOrElse("announce-proxy-commands", true);
|
this.logPlayerConnections = logPlayerConnections;
|
||||||
this.logCommandExecutions = config.getOrElse("log-command-executions", false);
|
this.acceptTransfers = acceptTransfers;
|
||||||
this.logPlayerConnections = config.getOrElse("log-player-connections", true);
|
|
||||||
this.acceptTransfers = config.getOrElse("accepts-transfers", false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int getCompressionThreshold() {
|
public int getCompressionThreshold() {
|
||||||
return compressionThreshold;
|
return compressionThreshold;
|
||||||
}
|
}
|
||||||
@@ -825,7 +660,7 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Query {
|
static class Query {
|
||||||
|
|
||||||
@Expose
|
@Expose
|
||||||
private boolean queryEnabled = false;
|
private boolean queryEnabled = false;
|
||||||
@@ -836,25 +671,16 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
@Expose
|
@Expose
|
||||||
private boolean showPlugins = false;
|
private boolean showPlugins = false;
|
||||||
|
|
||||||
private Query() {
|
Query() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Query(boolean queryEnabled, int queryPort, String queryMap, boolean showPlugins) {
|
Query(boolean queryEnabled, int queryPort, String queryMap, boolean showPlugins) {
|
||||||
this.queryEnabled = queryEnabled;
|
this.queryEnabled = queryEnabled;
|
||||||
this.queryPort = queryPort;
|
this.queryPort = queryPort;
|
||||||
this.queryMap = queryMap;
|
this.queryMap = queryMap;
|
||||||
this.showPlugins = showPlugins;
|
this.showPlugins = showPlugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Query(CommentedConfig config) {
|
|
||||||
if (config != null) {
|
|
||||||
this.queryEnabled = config.getOrElse("enabled", false);
|
|
||||||
this.queryPort = config.getIntOrElse("port", 25565);
|
|
||||||
this.queryMap = config.getOrElse("map", "Velocity");
|
|
||||||
this.showPlugins = config.getOrElse("show-plugins", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isQueryEnabled() {
|
public boolean isQueryEnabled() {
|
||||||
return queryEnabled;
|
return queryEnabled;
|
||||||
}
|
}
|
||||||
@@ -889,10 +715,11 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
|
|
||||||
private boolean enabled = true;
|
private boolean enabled = true;
|
||||||
|
|
||||||
private Metrics(CommentedConfig toml) {
|
Metrics() {
|
||||||
if (toml != null) {
|
|
||||||
this.enabled = toml.getOrElse("enabled", true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Metrics(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
|
|||||||
Reference in New Issue
Block a user