3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-12-24 15:20:35 +01:00

Add configuration stuff (this is not done yet)

Dieser Commit ist enthalten in:
Andrew Steinborn 2018-07-27 13:32:15 -04:00
Ursprung 66dcb13b5a
Commit f34e9b19c9
14 geänderte Dateien mit 407 neuen und 14 gelöschten Zeilen

3
.gitignore vendored
Datei anzeigen

@ -120,4 +120,5 @@ gradle-app.setting
# End of https://www.gitignore.io/api/java,gradle,intellij
# Other trash
logs/
logs/
velocity.toml

Datei anzeigen

@ -27,6 +27,7 @@ dependencies {
compile 'net.kyori:text:1.12-1.5.0'
compile 'org.apache.logging.log4j:log4j-api:2.11.0'
compile 'org.apache.logging.log4j:log4j-core:2.11.0'
compile 'com.moandjiezana.toml:toml4j:0.7.2'
testCompile 'org.junit.jupiter:junit-jupiter-api:5.3.0-M1'
testCompile 'org.junit.jupiter:junit-jupiter-engine:5.3.0-M1'
}

Datei anzeigen

@ -3,6 +3,7 @@ package com.velocitypowered.proxy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.velocitypowered.network.ConnectionManager;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.http.NettyHttpClient;
import com.velocitypowered.proxy.util.EncryptionUtils;
import io.netty.bootstrap.Bootstrap;
@ -11,7 +12,9 @@ import net.kyori.text.serializer.GsonComponentSerializer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Paths;
import java.security.KeyPair;
public class VelocityServer {
@ -22,6 +25,7 @@ public class VelocityServer {
.create();
private final ConnectionManager cm = new ConnectionManager();
private VelocityConfiguration configuration;
private NettyHttpClient httpClient;
private KeyPair serverKeyPair;
@ -36,9 +40,23 @@ public class VelocityServer {
return serverKeyPair;
}
public VelocityConfiguration getConfiguration() {
return configuration;
}
public void start() {
// Create a key pair
logger.info("Booting up Velocity...");
try {
configuration = VelocityConfiguration.read(Paths.get("velocity.toml"));
if (!configuration.validate()) {
logger.error("Your configuration is invalid. Velocity will refuse to start up until the errors are resolved.");
System.exit(1);
}
} catch (IOException e) {
logger.error("Unable to load your velocity.toml. The server will shut down.", e);
System.exit(1);
}
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
httpClient = new NettyHttpClient(this);

Datei anzeigen

@ -0,0 +1,7 @@
package com.velocitypowered.proxy.config;
public enum IPForwardingMode {
NONE,
LEGACY,
MODERN
}

Datei anzeigen

@ -0,0 +1,163 @@
package com.velocitypowered.proxy.config;
import com.google.common.collect.ImmutableMap;
import com.moandjiezana.toml.Toml;
import com.velocitypowered.proxy.util.LegacyChatColorUtils;
import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.Reader;
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.Map;
public class VelocityConfiguration {
private static final Logger logger = LogManager.getLogger(VelocityConfiguration.class);
private final String bind;
private final String motd;
private final int showMaxPlayers;
private final boolean onlineMode;
private final IPForwardingMode ipForwardingMode;
private final Map<String, String> servers;
private final List<String> attemptConnectionOrder;
private Component motdAsComponent;
private VelocityConfiguration(String bind, String motd, int showMaxPlayers, boolean onlineMode,
IPForwardingMode ipForwardingMode, Map<String, String> servers,
List<String> attemptConnectionOrder) {
this.bind = bind;
this.motd = motd;
this.showMaxPlayers = showMaxPlayers;
this.onlineMode = onlineMode;
this.ipForwardingMode = ipForwardingMode;
this.servers = servers;
this.attemptConnectionOrder = attemptConnectionOrder;
}
public boolean validate() {
boolean valid = true;
if (bind.isEmpty()) {
logger.error("'bind' option is empty.");
valid = false;
}
if (!onlineMode) {
logger.info("Proxy is running in offline mode!");
}
switch (ipForwardingMode) {
case NONE:
logger.info("IP forwarding is disabled! All players will appear to be connecting from the proxy and will have offline-mode UUIDs.");
break;
case MODERN:
logger.warn("Modern IP forwarding is not currently implemented.");
break;
}
if (servers.isEmpty()) {
logger.error("You have no servers configured. :(");
valid = false;
} else {
if (attemptConnectionOrder.isEmpty()) {
logger.error("No fallback servers are configured!");
valid = false;
}
for (String s : attemptConnectionOrder) {
if (!servers.containsKey(s)) {
logger.error("Fallback server " + s + " doesn't exist!");
valid = false;
}
}
}
return valid;
}
public String getBind() {
return bind;
}
public String getMotd() {
return motd;
}
public Component getMotdComponent() {
if (motdAsComponent == null) {
if (motd.startsWith("{")) {
motdAsComponent = ComponentSerializers.JSON.deserialize(motd);
} else {
motdAsComponent = ComponentSerializers.LEGACY.deserialize(LegacyChatColorUtils.translate('&', motd));
}
}
return motdAsComponent;
}
public int getShowMaxPlayers() {
return showMaxPlayers;
}
public boolean isOnlineMode() {
return onlineMode;
}
public IPForwardingMode getIpForwardingMode() {
return ipForwardingMode;
}
public Map<String, String> getServers() {
return servers;
}
public List<String> getAttemptConnectionOrder() {
return attemptConnectionOrder;
}
@Override
public String toString() {
return "VelocityConfiguration{" +
"bind='" + bind + '\'' +
", motd='" + motd + '\'' +
", showMaxPlayers=" + showMaxPlayers +
", onlineMode=" + onlineMode +
", ipForwardingMode=" + ipForwardingMode +
", servers=" + servers +
", attemptConnectionOrder=" + attemptConnectionOrder +
'}';
}
public static VelocityConfiguration read(Path path) throws IOException {
try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
Toml toml = new Toml().read(reader);
Map<String, String> servers = new HashMap<>();
for (Map.Entry<String, Object> entry : toml.getTable("servers").entrySet()) {
if (entry.getValue() instanceof String) {
servers.put(entry.getKey(), (String) entry.getValue());
} else {
if (!entry.getKey().equalsIgnoreCase("try")) {
throw new IllegalArgumentException("Server entry " + entry.getKey() + " is not a string!");
}
}
}
return new VelocityConfiguration(
toml.getString("bind"),
toml.getString("motd"),
toml.getLong("show-max-players").intValue(),
toml.getBoolean("online-mode"),
IPForwardingMode.valueOf(toml.getString("ip-forwarding").toUpperCase()),
ImmutableMap.copyOf(servers),
toml.getTable("servers").getList("try"));
}
}
}

Datei anzeigen

@ -97,6 +97,16 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
channel.writeAndFlush(msg, channel.voidPromise());
}
public void delayedWrite(Object msg) {
ensureOpen();
channel.write(msg, channel.voidPromise());
}
public void flush() {
ensureOpen();
channel.flush();
}
public void closeWith(Object msg) {
ensureOpen();
teardown();

Datei anzeigen

@ -1,5 +1,6 @@
package com.velocitypowered.proxy.connection.backend;
import com.velocitypowered.proxy.config.IPForwardingMode;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder;
@ -86,7 +87,11 @@ public class ServerConnection {
Handshake handshake = new Handshake();
handshake.setNextStatus(2); // login
handshake.setProtocolVersion(proxyPlayer.getConnection().getProtocolVersion());
handshake.setServerAddress(createBungeeForwardingAddress());
if (VelocityServer.getServer().getConfiguration().getIpForwardingMode() == IPForwardingMode.LEGACY) {
handshake.setServerAddress(createBungeeForwardingAddress());
} else {
handshake.setServerAddress(serverInfo.getAddress().getHostString());
}
handshake.setPort(serverInfo.getAddress().getPort());
channel.write(handshake);

Datei anzeigen

@ -95,10 +95,11 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// - Another respawn with the correct dimension
// We can't simply ignore the packet with the different dimension. If you try to be smart about it it doesn't
// work.
player.getConnection().write(joinGame);
player.getConnection().delayedWrite(joinGame);
int tempDim = joinGame.getDimension() == 0 ? -1 : 0;
player.getConnection().write(new Respawn(tempDim, joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType()));
player.getConnection().write(new Respawn(joinGame.getDimension(), joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType()));
player.getConnection().delayedWrite(new Respawn(tempDim, joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType()));
player.getConnection().delayedWrite(new Respawn(joinGame.getDimension(), joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType()));
player.getConnection().flush();
currentDimension = joinGame.getDimension();
}
}

Datei anzeigen

@ -41,13 +41,15 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
if (packet instanceof ServerLogin) {
this.login = (ServerLogin) packet;
// Request encryption.
EncryptionRequest request = generateRequest();
this.verify = Arrays.copyOf(request.getVerifyToken(), 4);
inbound.write(request);
// TODO: Online-mode checks
//handleSuccessfulLogin();
if (VelocityServer.getServer().getConfiguration().isOnlineMode()) {
// Request encryption.
EncryptionRequest request = generateRequest();
this.verify = Arrays.copyOf(request.getVerifyToken(), 4);
inbound.write(request);
} else {
// Offline-mode, don't try to request encryption.
handleSuccessfulLogin(GameProfile.forOfflinePlayer(login.getUsername()));
}
}
if (packet instanceof EncryptionResponse) {

Datei anzeigen

@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.packets.Ping;
import com.velocitypowered.proxy.protocol.packets.StatusRequest;
@ -33,11 +34,13 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
return;
}
VelocityConfiguration configuration = VelocityServer.getServer().getConfiguration();
// Status request
ServerPing ping = new ServerPing(
new ServerPing.Version(340, "1.12.2"),
new ServerPing.Players(0, 0),
TextComponent.of("test"),
new ServerPing.Players(0, configuration.getShowMaxPlayers()),
configuration.getMotdComponent(),
null
);
StatusResponse response = new StatusResponse();

Datei anzeigen

@ -0,0 +1,56 @@
package com.velocitypowered.proxy.util;
import com.google.common.base.Preconditions;
import java.util.regex.Pattern;
/**
* Utilities for handling legacy Minecraft color codes. Generally, you should prefer JSON-based components, but for
* convenience Velocity provides these utilities.
*/
public enum LegacyChatColorUtils {
;
public static final char FORMAT_CHAR = '\u00a7';
private static final Pattern CHAT_COLOR_MATCHER = Pattern.compile("(?i)" + Character.toString(FORMAT_CHAR) + "[0-9A-FL-OR]");
/**
* Translates a string with Minecraft color codes prefixed with a different character than the section symbol into
* a string that uses the section symbol.
* @param originalChar the char the color codes are prefixed by
* @param text the text to translate
* @return the translated text
*/
public static String translate(char originalChar, String text) {
Preconditions.checkNotNull(text, "text");
char[] textChars = text.toCharArray();
int foundSectionIdx = -1;
for (int i = 0; i < textChars.length; i++) {
char textChar = textChars[i];
if (textChar == originalChar) {
foundSectionIdx = i;
continue;
}
if (foundSectionIdx >= 0) {
textChar = Character.toLowerCase(textChar);
if ((textChar >= 'a' && textChar <= 'f') || (textChar >= '0' && textChar <= '9') ||
(textChar >= 'l' && textChar <= 'o' || textChar == 'r')) {
textChars[foundSectionIdx] = FORMAT_CHAR;
}
foundSectionIdx = -1;
}
}
return new String(textChars);
}
/**
* Removes all Minecraft color codes from the string.
* @param text the text to remove color codes from
* @return a new String without Minecraft color codes
*/
public static String removeFormatting(String text) {
Preconditions.checkNotNull(text, "text");
return CHAT_COLOR_MATCHER.matcher(text).replaceAll("");
}
}

Datei anzeigen

@ -0,0 +1,33 @@
# What port should the proxy be bound to? By default, we'll bind to all addresses on port 25577.
bind = "0.0.0.0:25577"
# What should be the MOTD? Legacy color codes and JSON are accepted.
motd = "&3A Velocity Server"
# What should we display for the maximum number of players? (Velocity does not support a cap
# on the number of players online.)
show-max-players = 500
# Should we authenticate players with Mojang? By default, this is on.
online-mode = true
# Should we forward IP addresses and other data to backend servers?
# Available options:
# - "none": No forwarding will be done. All players will appear to be connecting from the proxy
# and will have offline-mode UUIDs.
# - "legacy": Forward player IPs and UUIDs in BungeeCord-compatible fashion. Use this if you run
# servers using Minecraft 1.12 or lower.
# - "modern": Forward player IPs and UUIDs as part of the login process using Velocity's native
# forwarding. Only applicable for Minecraft 1.13 or higher.
ip-forwarding = "modern"
[servers]
# Configure your servers here.
lobby = "127.0.0.1:30066"
factions = "127.0.0.1:30067"
minigames = "127.0.0.1:30068"
# In what order we should try servers when a player logs in or is kicked from a server.
try = [
"lobby"
]

Datei anzeigen

@ -0,0 +1,61 @@
package com.velocitypowered.proxy.util;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class LegacyChatColorUtilsTest {
private static final String NON_FORMATTED = "Velocity";
private static final String FORMATTED = "\u00a7cVelocity";
private static final String FORMATTED_MULTIPLE = "\u00a7c\u00a7lVelocity";
private static final String FORMATTED_MULTIPLE_VARIED = "\u00a7c\u00a7lVelo\u00a7a\u00a7mcity";
private static final String INVALID = "\u00a7gVelocity";
private static final String RAW_SECTION = "\u00a7";
@Test
void removeFormattingNonFormatted() {
assertEquals(NON_FORMATTED, LegacyChatColorUtils.removeFormatting(NON_FORMATTED));
}
@Test
void removeFormattingFormatted() {
assertEquals(NON_FORMATTED, LegacyChatColorUtils.removeFormatting(FORMATTED));
}
@Test
void removeFormattingFormattedMultiple() {
assertEquals(NON_FORMATTED, LegacyChatColorUtils.removeFormatting(FORMATTED_MULTIPLE));
}
@Test
void removeFormattingFormattedMultipleVaried() {
assertEquals(NON_FORMATTED, LegacyChatColorUtils.removeFormatting(FORMATTED_MULTIPLE_VARIED));
}
@Test
void removeFormattingInvalidFormat() {
assertEquals(INVALID, LegacyChatColorUtils.removeFormatting(INVALID));
}
@Test
void removeFormattingRawSection() {
assertEquals(RAW_SECTION, LegacyChatColorUtils.removeFormatting(RAW_SECTION));
}
@Test
void translate() {
assertEquals(FORMATTED, LegacyChatColorUtils.translate('&', "&cVelocity"));
}
@Test
void translateMultiple() {
assertEquals(FORMATTED_MULTIPLE, LegacyChatColorUtils.translate('&', "&c&lVelocity"));
assertEquals(FORMATTED_MULTIPLE_VARIED, LegacyChatColorUtils.translate('&', "&c&lVelo&a&mcity"));
}
@Test
void translateDifferentChar() {
assertEquals(FORMATTED, LegacyChatColorUtils.translate('$', "$cVelocity"));
assertEquals(FORMATTED_MULTIPLE_VARIED, LegacyChatColorUtils.translate('$', "$c$lVelo$a$mcity"));
}
}

32
velocity.toml Normale Datei
Datei anzeigen

@ -0,0 +1,32 @@
# What port should the proxy be bound to? By default, we'll bind to all addresses on port 25577.
bind = "0.0.0.0:26671"
# What should be the MOTD? Legacy color codes and JSON are accepted.
motd = "&3A Velocity Server"
# What should we display for the maximum number of players? (Velocity does not support a cap
# on the number of players online.)
show-max-players = 500
# Should we authenticate players with Mojang? By default, this is on.
online-mode = true
# Should we forward IP addresses and other data to backend servers?
# Available options:
# - "none": No forwarding will be done. All players will appear to be connecting from the proxy
# and will have offline-mode UUIDs.
# - "legacy": Forward player IPs and UUIDs in BungeeCord-compatible fashion. Use this if you run
# servers using Minecraft 1.12 or lower.
# - "modern": Forward player IPs and UUIDs as part of the login process using Velocity's native
# forwarding. Only applicable for Minecraft 1.13 or higher.
ip-forwarding = "legacy"
[servers]
# Configure your servers here.
lobby = "127.0.0.1:25565"
lobby2 = "127.0.0.1:25566"
# In what order we should try servers when a player logs in or is kicked from a server.
try = [
"lobby"
]