Skip to content

Commit b99f6c7

Browse files
committed
Seperate out toml4j loading logic
1 parent 08a42b3 commit b99f6c7

File tree

4 files changed

+355
-223
lines changed

4 files changed

+355
-223
lines changed

proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import com.velocitypowered.proxy.command.builtin.ServerCommand;
4646
import com.velocitypowered.proxy.command.builtin.ShutdownCommand;
4747
import com.velocitypowered.proxy.command.builtin.VelocityCommand;
48+
import com.velocitypowered.proxy.config.LegacyConfigurationLoader;
4849
import com.velocitypowered.proxy.config.VelocityConfiguration;
4950
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
5051
import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
@@ -383,7 +384,7 @@ private void registerTranslations() {
383384
private void doStartupConfigLoad() {
384385
try {
385386
Path configPath = Path.of("velocity.toml");
386-
configuration = VelocityConfiguration.read(configPath);
387+
configuration = LegacyConfigurationLoader.read(configPath);
387388

388389
if (!configuration.validate()) {
389390
logger.error("Your configuration is invalid. Velocity will not start up until the errors "
@@ -461,7 +462,7 @@ public boolean isShutdown() {
461462
*/
462463
public boolean reloadConfiguration() throws IOException {
463464
Path configPath = Path.of("velocity.toml");
464-
VelocityConfiguration newConfiguration = VelocityConfiguration.read(configPath);
465+
VelocityConfiguration newConfiguration = LegacyConfigurationLoader.read(configPath);
465466

466467
if (!newConfiguration.validate()) {
467468
return false;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright (C) 2024 Velocity Contributors
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.velocitypowered.proxy.config;
19+
20+
/**
21+
* Velocity Configurate Loader entry utils.
22+
*/
23+
public class ConfigurationLoader {
24+
25+
private ConfigurationLoader() {
26+
}
27+
28+
29+
/**
30+
* performs legacy configuration migration if needed.
31+
*
32+
* @return {@code true} if a migration was performed, {@code false} otherwise
33+
*/
34+
public static boolean migrateIfNeeded() {
35+
return false;
36+
}
37+
38+
/**
39+
* loads the velocity configuration.
40+
*
41+
* @return the loaded configuration
42+
*/
43+
public static VelocityConfiguration loadConfiguration() {
44+
return null;
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
/*
2+
* Copyright (C) 2024 Velocity Contributors
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.velocitypowered.proxy.config;
19+
20+
import static com.velocitypowered.proxy.config.VelocityConfiguration.Servers.cleanServerName;
21+
import static com.velocitypowered.proxy.config.VelocityConfiguration.generateRandomString;
22+
23+
import com.electronwill.nightconfig.core.CommentedConfig;
24+
import com.electronwill.nightconfig.core.UnmodifiableConfig;
25+
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
26+
import com.google.common.collect.ImmutableList;
27+
import com.google.common.collect.ImmutableMap;
28+
import com.velocitypowered.proxy.config.migration.ConfigurationMigration;
29+
import com.velocitypowered.proxy.config.migration.ForwardingMigration;
30+
import com.velocitypowered.proxy.config.migration.KeyAuthenticationMigration;
31+
import com.velocitypowered.proxy.config.migration.MotdMigration;
32+
import com.velocitypowered.proxy.config.migration.TransferIntegrationMigration;
33+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
34+
import java.io.IOException;
35+
import java.net.URL;
36+
import java.nio.charset.StandardCharsets;
37+
import java.nio.file.Files;
38+
import java.nio.file.Path;
39+
import java.util.HashMap;
40+
import java.util.List;
41+
import java.util.Locale;
42+
import java.util.Map;
43+
import org.apache.logging.log4j.LogManager;
44+
import org.apache.logging.log4j.Logger;
45+
46+
/**
47+
* Legacy configuration loader for Velocity.
48+
*/
49+
public class LegacyConfigurationLoader {
50+
private static final Logger logger = LogManager.getLogger(LegacyConfigurationLoader.class);
51+
52+
/**
53+
* Reads the Velocity configuration from {@code path}.
54+
*
55+
* @param path the path to read from
56+
* @return the deserialized Velocity configuration
57+
* @throws IOException if we could not read from the {@code path}.
58+
*/
59+
@SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE",
60+
justification = "I looked carefully and there's no way SpotBugs is right.")
61+
public static VelocityConfiguration read(Path path) throws IOException {
62+
URL defaultConfigLocation = VelocityConfiguration.class.getClassLoader()
63+
.getResource("default-velocity.toml");
64+
if (defaultConfigLocation == null) {
65+
throw new RuntimeException("Default configuration file does not exist.");
66+
}
67+
68+
// Create the forwarding-secret file on first-time startup if it doesn't exist
69+
final Path defaultForwardingSecretPath = Path.of("forwarding.secret");
70+
if (Files.notExists(path) && Files.notExists(defaultForwardingSecretPath)) {
71+
Files.writeString(defaultForwardingSecretPath, generateRandomString(12));
72+
}
73+
74+
try (final CommentedFileConfig config = CommentedFileConfig.builder(path)
75+
.defaultData(defaultConfigLocation)
76+
.autosave()
77+
.preserveInsertionOrder()
78+
.sync()
79+
.build()
80+
) {
81+
config.load();
82+
83+
final ConfigurationMigration[] migrations = {
84+
new ForwardingMigration(),
85+
new KeyAuthenticationMigration(),
86+
new MotdMigration(),
87+
new TransferIntegrationMigration()
88+
};
89+
90+
for (final ConfigurationMigration migration : migrations) {
91+
if (migration.shouldMigrate(config)) {
92+
migration.migrate(config, logger);
93+
}
94+
}
95+
96+
String forwardingSecretString = System.getenv().getOrDefault(
97+
"VELOCITY_FORWARDING_SECRET", "");
98+
if (forwardingSecretString.isEmpty()) {
99+
final String forwardSecretFile = config.get("forwarding-secret-file");
100+
final Path secretPath = forwardSecretFile == null
101+
? defaultForwardingSecretPath
102+
: Path.of(forwardSecretFile);
103+
if (Files.exists(secretPath)) {
104+
if (Files.isRegularFile(secretPath)) {
105+
forwardingSecretString = String.join("", Files.readAllLines(secretPath));
106+
} else {
107+
throw new RuntimeException(
108+
"The file " + forwardSecretFile + " is not a valid file or it is a directory.");
109+
}
110+
} else {
111+
throw new RuntimeException("The forwarding-secret-file does not exist.");
112+
}
113+
}
114+
final byte[] forwardingSecret = forwardingSecretString.getBytes(StandardCharsets.UTF_8);
115+
final String motd = config.getOrElse("motd", "<#09add3>A Velocity Server");
116+
117+
// Read the rest of the config
118+
final CommentedConfig serversConfig = config.get("servers");
119+
final CommentedConfig forcedHostsConfig = config.get("forced-hosts");
120+
final CommentedConfig advancedConfig = config.get("advanced");
121+
final CommentedConfig queryConfig = config.get("query");
122+
final CommentedConfig metricsConfig = config.get("metrics");
123+
final PlayerInfoForwarding forwardingMode = config.getEnumOrElse(
124+
"player-info-forwarding-mode", PlayerInfoForwarding.NONE);
125+
final PingPassthroughMode pingPassthroughMode = config.getEnumOrElse("ping-passthrough",
126+
PingPassthroughMode.DISABLED);
127+
128+
final String bind = config.getOrElse("bind", "0.0.0.0:25565");
129+
final int maxPlayers = config.getIntOrElse("show-max-players", 500);
130+
final boolean onlineMode = config.getOrElse("online-mode", true);
131+
final boolean forceKeyAuthentication = config.getOrElse("force-key-authentication", true);
132+
final boolean announceForge = config.getOrElse("announce-forge", true);
133+
final boolean preventClientProxyConnections = config.getOrElse(
134+
"prevent-client-proxy-connections", false);
135+
final boolean kickExisting = config.getOrElse("kick-existing-players", false);
136+
final boolean enablePlayerAddressLogging = config.getOrElse(
137+
"enable-player-address-logging", true);
138+
139+
// Throw an exception if the forwarding-secret file is empty and the proxy is using a
140+
// forwarding mode that requires it.
141+
if (forwardingSecret.length == 0
142+
&& (forwardingMode == PlayerInfoForwarding.MODERN
143+
|| forwardingMode == PlayerInfoForwarding.BUNGEEGUARD)) {
144+
throw new RuntimeException("The forwarding-secret file must not be empty.");
145+
}
146+
147+
return new VelocityConfiguration(
148+
bind,
149+
motd,
150+
maxPlayers,
151+
onlineMode,
152+
preventClientProxyConnections,
153+
announceForge,
154+
forwardingMode,
155+
forwardingSecret,
156+
kickExisting,
157+
pingPassthroughMode,
158+
enablePlayerAddressLogging,
159+
readServers(serversConfig),
160+
readForcedHosts(forcedHostsConfig),
161+
readAdvanced(advancedConfig),
162+
readQuery(queryConfig),
163+
readMetrics(metricsConfig),
164+
forceKeyAuthentication
165+
);
166+
}
167+
}
168+
169+
170+
private static VelocityConfiguration.Servers readServers(CommentedConfig config) {
171+
if (config != null) {
172+
Map<String, String> servers = new HashMap<>();
173+
for (UnmodifiableConfig.Entry entry : config.entrySet()) {
174+
if (entry.getValue() instanceof String) {
175+
servers.put(cleanServerName(entry.getKey()), entry.getValue());
176+
} else {
177+
if (!entry.getKey().equalsIgnoreCase("try")) {
178+
throw new IllegalArgumentException(
179+
"Server entry " + entry.getKey() + " is not a string!");
180+
}
181+
}
182+
}
183+
return new VelocityConfiguration.Servers(ImmutableMap.copyOf(servers),
184+
config.getOrElse("try", ImmutableList.of("lobby")));
185+
}
186+
return new VelocityConfiguration.Servers();
187+
}
188+
189+
private static VelocityConfiguration.ForcedHosts readForcedHosts(CommentedConfig config) {
190+
if (config != null) {
191+
Map<String, List<String>> forcedHosts = new HashMap<>();
192+
for (UnmodifiableConfig.Entry entry : config.entrySet()) {
193+
if (entry.getValue() instanceof String) {
194+
forcedHosts.put(entry.getKey().toLowerCase(Locale.ROOT),
195+
ImmutableList.of(entry.getValue()));
196+
} else if (entry.getValue() instanceof List) {
197+
forcedHosts.put(entry.getKey().toLowerCase(Locale.ROOT),
198+
ImmutableList.copyOf((List<String>) entry.getValue()));
199+
} else {
200+
throw new IllegalStateException(
201+
"Invalid value of type " + entry.getValue().getClass() + " in forced hosts!");
202+
}
203+
}
204+
return new VelocityConfiguration.ForcedHosts(forcedHosts);
205+
}
206+
return new VelocityConfiguration.ForcedHosts();
207+
}
208+
209+
private static VelocityConfiguration.Advanced readAdvanced(CommentedConfig config) {
210+
if (config != null) {
211+
int compressionThreshold = config.getIntOrElse("compression-threshold", 256);
212+
int compressionLevel = config.getIntOrElse("compression-level", -1);
213+
int loginRatelimit = config.getIntOrElse("login-ratelimit", 3000);
214+
int connectionTimeout = config.getIntOrElse("connection-timeout", 5000);
215+
int readTimeout = config.getIntOrElse("read-timeout", 30000);
216+
boolean proxyProtocol = false;
217+
if (config.contains("haproxy-protocol")) {
218+
proxyProtocol = config.getOrElse("haproxy-protocol", false);
219+
} else {
220+
proxyProtocol = config.getOrElse("proxy-protocol", false);
221+
}
222+
boolean tcpFastOpen = config.getOrElse("tcp-fast-open", false);
223+
boolean bungeePluginMessageChannel = config.getOrElse("bungee-plugin-message-channel", true);
224+
boolean showPingRequests = config.getOrElse("show-ping-requests", false);
225+
boolean failoverOnUnexpectedServerDisconnect = config
226+
.getOrElse("failover-on-unexpected-server-disconnect", true);
227+
boolean announceProxyCommands = config.getOrElse("announce-proxy-commands", true);
228+
boolean logCommandExecutions = config.getOrElse("log-command-executions", false);
229+
boolean logPlayerConnections = config.getOrElse("log-player-connections", true);
230+
boolean acceptTransfers = config.getOrElse("accepts-transfers", false);
231+
return new VelocityConfiguration.Advanced(compressionThreshold, compressionLevel,
232+
loginRatelimit, connectionTimeout, readTimeout, proxyProtocol, tcpFastOpen,
233+
bungeePluginMessageChannel, showPingRequests, failoverOnUnexpectedServerDisconnect,
234+
announceProxyCommands, logCommandExecutions, logPlayerConnections, acceptTransfers);
235+
}
236+
return new VelocityConfiguration.Advanced();
237+
}
238+
239+
private static VelocityConfiguration.Query readQuery(CommentedConfig config) {
240+
if (config != null) {
241+
boolean queryEnabled = config.getOrElse("enabled", false);
242+
int queryPort = config.getIntOrElse("port", 25565);
243+
String queryMap = config.getOrElse("map", "Velocity");
244+
boolean showPlugins = config.getOrElse("show-plugins", false);
245+
return new VelocityConfiguration.Query(queryEnabled, queryPort, queryMap, showPlugins);
246+
}
247+
return new VelocityConfiguration.Query();
248+
}
249+
250+
private static VelocityConfiguration.Metrics readMetrics(CommentedConfig config) {
251+
if (config != null) {
252+
boolean enabled = config.getOrElse("enabled", true);
253+
return new VelocityConfiguration.Metrics(enabled);
254+
}
255+
return new VelocityConfiguration.Metrics();
256+
}
257+
258+
}

0 commit comments

Comments
 (0)