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:
Ursprung
66dcb13b5a
Commit
f34e9b19c9
3
.gitignore
vendored
3
.gitignore
vendored
@ -120,4 +120,5 @@ gradle-app.setting
|
||||
# End of https://www.gitignore.io/api/java,gradle,intellij
|
||||
|
||||
# Other trash
|
||||
logs/
|
||||
logs/
|
||||
velocity.toml
|
@ -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'
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.velocitypowered.proxy.config;
|
||||
|
||||
public enum IPForwardingMode {
|
||||
NONE,
|
||||
LEGACY,
|
||||
MODERN
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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("");
|
||||
}
|
||||
}
|
33
src/main/resources/velocity.toml
Normale Datei
33
src/main/resources/velocity.toml
Normale Datei
@ -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"
|
||||
]
|
@ -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
32
velocity.toml
Normale Datei
@ -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"
|
||||
]
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren