diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 07f49bf1..865c9390 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -6,16 +6,16 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: persist-credentials: false - name: Set up Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 - name: Set up JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 21 distribution: 'zulu' diff --git a/README.md b/README.md index 5de7436f..aa6527f0 100644 --- a/README.md +++ b/README.md @@ -34,3 +34,9 @@ and you can configure it from there. Alternatively, you can get the proxy JAR from the [downloads](https://papermc.io/downloads/velocity) page. + +# Localisation + +Translations are handled using [Crowdin](https://papermc-io.crowdin.com/velocity). +If you want to translate a language not available on Crowdin, +you might want to ask in the [Discord](https://discord.gg/papermc) about it. diff --git a/api/build.gradle.kts b/api/build.gradle.kts index b96208c1..268a8197 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -59,11 +59,11 @@ tasks { val o = options as StandardJavadocDocletOptions o.encoding = "UTF-8" - o.source = "17" + o.source = "21" o.use() o.links( - "https://www.slf4j.org/apidocs/", + "https://www.javadocs.dev/org.slf4j/slf4j-api/${libs.slf4j.get().version}/", "https://guava.dev/releases/${libs.guava.get().version}/api/docs/", "https://google.github.io/guice/api-docs/${libs.guice.get().version}/javadoc/", "https://docs.oracle.com/en/java/javase/17/docs/api/", diff --git a/api/src/ap/java/com/velocitypowered/api/plugin/ap/SerializedPluginDescription.java b/api/src/ap/java/com/velocitypowered/api/plugin/ap/SerializedPluginDescription.java index da0a1ae4..b712d896 100644 --- a/api/src/ap/java/com/velocitypowered/api/plugin/ap/SerializedPluginDescription.java +++ b/api/src/ap/java/com/velocitypowered/api/plugin/ap/SerializedPluginDescription.java @@ -24,7 +24,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; */ public final class SerializedPluginDescription { - public static final Pattern ID_PATTERN = Pattern.compile("[a-z][a-z0-9-_]{0,63}"); + public static final String ID_PATTERN_STRING = "[a-z][a-z0-9-_]{0,63}"; + public static final Pattern ID_PATTERN = Pattern.compile(ID_PATTERN_STRING); // @Nullable is used here to make GSON skip these in the serialized file private final String id; diff --git a/api/src/main/java/com/velocitypowered/api/command/CommandSource.java b/api/src/main/java/com/velocitypowered/api/command/CommandSource.java index 3cc03925..c94dec4c 100644 --- a/api/src/main/java/com/velocitypowered/api/command/CommandSource.java +++ b/api/src/main/java/com/velocitypowered/api/command/CommandSource.java @@ -23,7 +23,7 @@ public interface CommandSource extends Audience, PermissionSubject { * Sends a message with the MiniMessage format to this source. * * @param message MiniMessage content - * @see MiniMessage docs + * @see MiniMessage docs * for more information on the format. **/ default void sendRichMessage(final @NotNull String message) { @@ -31,14 +31,14 @@ public interface CommandSource extends Audience, PermissionSubject { } /** - * Sends a message with the MiniMessage format to this source. - * - * @param message MiniMessage content - * @param resolvers resolvers to use - * @see MiniMessage docs - * and MiniMessage Placeholders docs - * for more information on the format. - **/ + * Sends a message with the MiniMessage format to this source. + * + * @param message MiniMessage content + * @param resolvers resolvers to use + * @see MiniMessage docs + * and MiniMessage Placeholders docs + * for more information on the format. + */ default void sendRichMessage( final @NotNull String message, final @NotNull TagResolver @NotNull... resolvers diff --git a/api/src/main/java/com/velocitypowered/api/event/connection/LoginEvent.java b/api/src/main/java/com/velocitypowered/api/event/connection/LoginEvent.java index 49408897..454da839 100644 --- a/api/src/main/java/com/velocitypowered/api/event/connection/LoginEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/connection/LoginEvent.java @@ -11,6 +11,7 @@ import com.google.common.base.Preconditions; import com.velocitypowered.api.event.ResultedEvent; import com.velocitypowered.api.event.annotation.AwaitingEvent; import com.velocitypowered.api.proxy.Player; +import org.checkerframework.checker.nullness.qual.Nullable; /** * This event is fired once the player has been authenticated, but before they connect to a server. @@ -22,10 +23,24 @@ import com.velocitypowered.api.proxy.Player; public final class LoginEvent implements ResultedEvent { private final Player player; + private final String serverIdHash; private ComponentResult result; + @Deprecated(forRemoval = true) public LoginEvent(Player player) { + this(player, null); + } + + /** + * Constructs a new {@link LoginEvent}. + * + * @param player the player who has completed authentication + * @param serverIdHash the server ID hash sent to Mojang for authentication, + * or {@code null} if the connection is in offline-mode + */ + public LoginEvent(Player player, @Nullable String serverIdHash) { this.player = Preconditions.checkNotNull(player, "player"); + this.serverIdHash = serverIdHash; this.result = ComponentResult.allowed(); } @@ -33,6 +48,16 @@ public final class LoginEvent implements ResultedEvent channels; + + public PlayerChannelUnregisterEvent(Player player, List channels) { + this.player = Preconditions.checkNotNull(player, "player"); + this.channels = Preconditions.checkNotNull(channels, "channels"); + } + + public Player getPlayer() { + return player; + } + + public List getChannels() { + return channels; + } + + @Override + public String toString() { + return "PlayerChannelUnregisterEvent{" + + "player=" + player + + ", channels=" + channels + + '}'; + } +} diff --git a/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java index 98bcb60d..5689ae8c 100644 --- a/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java @@ -143,7 +143,7 @@ public final class ServerPreConnectEvent implements * is used, then {@link ConnectionRequestBuilder#connect()}'s result will have the status * {@link Status#CONNECTION_CANCELLED}. * - * @return a result to deny conneections + * @return a result to deny connections */ public static ServerResult denied() { return DENIED; diff --git a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java index f2a629b1..b166291b 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -93,7 +93,9 @@ public enum ProtocolVersion implements Ordered { MINECRAFT_1_21_5(770, "1.21.5"), MINECRAFT_1_21_6(771, "1.21.6"), MINECRAFT_1_21_7(772, "1.21.7", "1.21.8"), - MINECRAFT_1_21_9(773, "1.21.9", "1.21.10"); + MINECRAFT_1_21_9(773, "1.21.9", "1.21.10"), + MINECRAFT_1_21_11(774, "1.21.11"), + MINECRAFT_26_1(775, "26.1", "26.1.1", "26.1.2"); private static final int SNAPSHOT_BIT = 30; diff --git a/api/src/main/java/com/velocitypowered/api/plugin/Dependency.java b/api/src/main/java/com/velocitypowered/api/plugin/Dependency.java index dfe1908f..413dd9e5 100644 --- a/api/src/main/java/com/velocitypowered/api/plugin/Dependency.java +++ b/api/src/main/java/com/velocitypowered/api/plugin/Dependency.java @@ -7,9 +7,11 @@ package com.velocitypowered.api.plugin; +import com.velocitypowered.api.plugin.ap.SerializedPluginDescription; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.intellij.lang.annotations.Pattern; /** * Indicates that the {@link Plugin} depends on another plugin in order to enable. @@ -24,6 +26,7 @@ public @interface Dependency { * @return The dependency plugin ID * @see Plugin#id() */ + @Pattern(SerializedPluginDescription.ID_PATTERN_STRING) String id(); /** diff --git a/api/src/main/java/com/velocitypowered/api/plugin/Plugin.java b/api/src/main/java/com/velocitypowered/api/plugin/Plugin.java index 523648e6..d3b458ca 100644 --- a/api/src/main/java/com/velocitypowered/api/plugin/Plugin.java +++ b/api/src/main/java/com/velocitypowered/api/plugin/Plugin.java @@ -7,10 +7,12 @@ package com.velocitypowered.api.plugin; +import com.velocitypowered.api.plugin.ap.SerializedPluginDescription; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.intellij.lang.annotations.Pattern; /** * Annotation used to describe a Velocity plugin. @@ -26,6 +28,7 @@ public @interface Plugin { * * @return the ID for this plugin */ + @Pattern(SerializedPluginDescription.ID_PATTERN_STRING) String id(); /** diff --git a/api/src/main/java/com/velocitypowered/api/proxy/Player.java b/api/src/main/java/com/velocitypowered/api/proxy/Player.java index efe21cd7..057b8a23 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -39,6 +39,7 @@ import net.kyori.adventure.sound.SoundStop; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.event.HoverEventSource; +import net.kyori.adventure.text.object.PlayerHeadObjectContents; import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.NotNull; @@ -49,7 +50,8 @@ public interface Player extends /* Fundamental Velocity interfaces */ CommandSource, InboundConnection, ChannelMessageSource, ChannelMessageSink, /* Adventure-specific interfaces */ - Identified, HoverEventSource, Keyed, KeyIdentifiable, Sound.Emitter { + Identified, HoverEventSource, Keyed, KeyIdentifiable, Sound.Emitter, + PlayerHeadObjectContents.SkinSource { /** * Returns the player's current username. @@ -336,6 +338,15 @@ public interface Player extends Component.text(getUsername())))); } + @SuppressWarnings("UnstableApiUsage") // permitted implementation + @Override + default void applySkinToPlayerHeadContents( + final PlayerHeadObjectContents.@NotNull Builder builder) { + builder.skin(this.getGameProfile()); + if (this.hasSentPlayerSettings()) { + builder.hat(this.getPlayerSettings().getSkinParts().hasHat()); + } + } /** * Gets the player's client brand. diff --git a/api/src/main/java/com/velocitypowered/api/util/GameProfile.java b/api/src/main/java/com/velocitypowered/api/util/GameProfile.java index 27c42138..5fd60e5b 100644 --- a/api/src/main/java/com/velocitypowered/api/util/GameProfile.java +++ b/api/src/main/java/com/velocitypowered/api/util/GameProfile.java @@ -11,11 +11,14 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; +import net.kyori.adventure.text.object.PlayerHeadObjectContents; +import org.jetbrains.annotations.NotNull; /** * Represents a Mojang game profile. This class is immutable. */ -public final class GameProfile { +public final class GameProfile implements PlayerHeadObjectContents.SkinSource { private final UUID id; private final String undashedId; @@ -169,6 +172,23 @@ public final class GameProfile { ImmutableList.of()); } + @SuppressWarnings("UnstableApiUsage") // permitted implementation + @Override + public void applySkinToPlayerHeadContents( + final PlayerHeadObjectContents.@NotNull Builder builder) { + if (this.properties.isEmpty()) { + builder.id(this.id); + return; + } + + builder.id(this.id) + .name(this.name) + .profileProperties(this.properties.stream() + .map(property -> PlayerHeadObjectContents.property(property.getName(), + property.getValue(), property.getSignature())) + .collect(Collectors.toList())); + } + @Override public String toString() { return "GameProfile{" diff --git a/build.gradle.kts b/build.gradle.kts index a761de5f..e01f345a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ subprojects { java { toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) + languageVersion.set(JavaLanguageVersion.of(21)) } } diff --git a/gradle.properties b/gradle.properties index 55c2ed10..d048feb7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ group=com.velocitypowered -version=3.4.0-SNAPSHOT +version=3.5.0-SNAPSHOT diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d0bb2c84..caa82d81 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,27 +1,26 @@ [versions] configurate3 = "3.7.3" -configurate4 = "4.1.2" +configurate4 = "4.2.0" flare = "2.0.1" -log4j = "2.24.3" -netty = "4.2.7.Final" +log4j = "2.25.3" +netty = "4.2.10.Final" [plugins] -fill = "io.papermc.fill.gradle:1.0.3" -indra-publishing = "net.kyori.indra.publishing:2.0.6" -shadow = "com.gradleup.shadow:8.3.6" -spotless = "com.diffplug.spotless:6.25.0" +fill = "io.papermc.fill.gradle:1.0.10" +shadow = "com.gradleup.shadow:9.3.1" +spotless = "com.diffplug.spotless:8.2.0" [libraries] -adventure-bom = "net.kyori:adventure-bom:4.25.0" -adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.25.0" -adventure-facet = "net.kyori:adventure-platform-facet:4.3.4" -asm = "org.ow2.asm:asm:9.8" -auto-service = "com.google.auto.service:auto-service:1.0.1" -auto-service-annotations = "com.google.auto.service:auto-service-annotations:1.0.1" +adventure-bom = "net.kyori:adventure-bom:4.26.1" +adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.26.1" +adventure-facet = "net.kyori:adventure-platform-facet:4.4.1" +asm = "org.ow2.asm:asm:9.9.1" +auto-service = "com.google.auto.service:auto-service:1.1.1" +auto-service-annotations = "com.google.auto.service:auto-service-annotations:1.1.1" brigadier = "com.velocitypowered:velocity-brigadier:1.0.0-SNAPSHOT" -bstats = "org.bstats:bstats-base:3.0.3" -caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.8" -checker-qual = "org.checkerframework:checker-qual:3.42.0" +bstats = "org.bstats:bstats-base:3.1.0" +caffeine = "com.github.ben-manes.caffeine:caffeine:3.2.3" +checker-qual = "org.checkerframework:checker-qual:3.53.0" checkstyle = "com.puppycrawl.tools:checkstyle:10.9.3" completablefutures = "com.spotify:completable-futures:0.3.6" configurate3-hocon = { module = "org.spongepowered:configurate-hocon", version.ref = "configurate3" } @@ -34,21 +33,21 @@ disruptor = "com.lmax:disruptor:4.0.0" fastutil = "it.unimi.dsi:fastutil:8.5.15" flare-core = { module = "space.vectrix.flare:flare", version.ref = "flare" } flare-fastutil = { module = "space.vectrix.flare:flare-fastutil", version.ref = "flare" } -jline = "org.jline:jline-terminal-jansi:3.30.2" +jline = "org.jline:jline-terminal-jansi:3.30.6" jopt = "net.sf.jopt-simple:jopt-simple:5.0.4" -junit = "org.junit.jupiter:junit-jupiter:5.10.2" -jspecify = "org.jspecify:jspecify:0.3.0" +junit = "org.junit.jupiter:junit-jupiter:5.14.2" +jspecify = "org.jspecify:jspecify:1.0.0" kyori-ansi = "net.kyori:ansi:1.1.1" -guava = "com.google.guava:guava:25.1-jre" -gson = "com.google.code.gson:gson:2.10.1" -guice = "com.google.inject:guice:6.0.0" +guava = "com.google.guava:guava:33.5.0-jre" +gson = "com.google.code.gson:gson:2.13.2" +guice = "com.google.inject:guice:7.0.0" lmbda = "org.lanternpowered:lmbda:2.0.0" log4j-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j" } log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" } log4j-slf4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" } log4j-iostreams = { module = "org.apache.logging.log4j:log4j-iostreams", version.ref = "log4j" } log4j-jul = { module = "org.apache.logging.log4j:log4j-jul", version.ref = "log4j" } -mockito = "org.mockito:mockito-core:5.10.0" +mockito = "org.mockito:mockito-core:5.21.0" netty-codec = { module = "io.netty:netty-codec", version.ref = "netty" } netty-codec-haproxy = { module = "io.netty:netty-codec-haproxy", version.ref = "netty" } netty-codec-http = { module = "io.netty:netty-codec-http", version.ref = "netty" } @@ -56,10 +55,10 @@ netty-handler = { module = "io.netty:netty-handler", version.ref = "netty" } netty-transport-native-epoll = { module = "io.netty:netty-transport-native-epoll", version.ref = "netty" } netty-transport-native-kqueue = { module = "io.netty:netty-transport-native-kqueue", version.ref = "netty" } netty-transport-native-iouring = { module = "io.netty:netty-transport-native-io_uring", version.ref = "netty" } -nightconfig = "com.electronwill.night-config:toml:3.6.7" +nightconfig = "com.electronwill.night-config:toml:3.8.3" slf4j = "org.slf4j:slf4j-api:2.0.17" -snakeyaml = "org.yaml:snakeyaml:1.33" -spotbugs-annotations = "com.github.spotbugs:spotbugs-annotations:4.7.3" +snakeyaml = "org.yaml:snakeyaml:2.5" +spotbugs-annotations = "com.github.spotbugs:spotbugs-annotations:4.9.8" terminalconsoleappender = "net.minecrell:terminalconsoleappender:1.3.0" [bundles] diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b95..61285a65 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ff23a68d..19a6bdeb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 5bdb31f8..adff685a 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -115,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -173,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -203,18 +200,17 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. @@ -249,4 +245,4 @@ eval "set -- $( tr '\n' ' ' )" '"$@"' -exec "$JAVACMD" "$@" \ No newline at end of file +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 6042cb3e..e509b2dd 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -36,7 +36,7 @@ set APP_HOME=%DIRNAME% for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS=-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m" +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell @@ -91,4 +90,4 @@ exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal -:omega \ No newline at end of file +:omega diff --git a/proxy/build.gradle.kts b/proxy/build.gradle.kts index 1559f5fd..c7164c15 100644 --- a/proxy/build.gradle.kts +++ b/proxy/build.gradle.kts @@ -10,7 +10,7 @@ plugins { application { mainClass.set("com.velocitypowered.proxy.Velocity") - applicationDefaultJvmArgs += listOf("-Dvelocity.packet-decode-logging=true"); + applicationDefaultJvmArgs += listOf("-Dvelocity.packet-decode-logging=true") } tasks { @@ -27,6 +27,10 @@ tasks { } shadowJar { + filesMatching("META-INF/org/apache/logging/log4j/core/config/plugins/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + transform(Log4j2PluginsCacheFileTransformer::class.java) // Exclude all the collection types we don"t intend to use @@ -131,7 +135,6 @@ fill { dependencies { implementation(project(":velocity-api")) implementation(project(":velocity-native")) - implementation(project(":velocity-proxy-log4j2-plugin")) implementation(libs.bundles.log4j) implementation(libs.kyori.ansi) @@ -168,4 +171,5 @@ dependencies { testImplementation(libs.mockito) annotationProcessor(libs.auto.service) + annotationProcessor(libs.log4j.core) } diff --git a/proxy/log4j2-plugin/build.gradle.kts b/proxy/log4j2-plugin/build.gradle.kts deleted file mode 100644 index 0a193944..00000000 --- a/proxy/log4j2-plugin/build.gradle.kts +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - implementation(libs.bundles.log4j) - annotationProcessor(libs.log4j.core) -} \ No newline at end of file diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index ccfc5f14..95f10bcb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -82,7 +82,6 @@ import java.net.http.HttpClient; import java.nio.file.Files; import java.nio.file.Path; import java.security.KeyPair; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -105,8 +104,8 @@ import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.translation.MiniMessageTranslationStore; import net.kyori.adventure.translation.GlobalTranslator; -import net.kyori.adventure.translation.TranslationStore; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.bstats.MetricsBase; @@ -242,8 +241,6 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { console.setupStreams(); pluginManager.registerPlugin(this.createVirtualPlugin()); - registerTranslations(); - // Yes, you're reading that correctly. We're generating a 1024-bit RSA keypair. Sounds // dangerous, right? We're well within the realm of factoring such a key... // @@ -292,6 +289,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { this.doStartupConfigLoad(); + registerTranslations(); + for (ServerInfo cliServer : options.getServers()) { servers.register(cliServer); } @@ -342,8 +341,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { } private void registerTranslations() { - final TranslationStore.StringBased translationRegistry = - TranslationStore.messageFormat(Key.key("velocity", "translations")); + final MiniMessageTranslationStore translationRegistry = + MiniMessageTranslationStore.create(Key.key("velocity", "translations")); translationRegistry.defaultLocale(Locale.US); try { ResourceUtils.visitResources(VelocityServer.class, path -> { @@ -835,7 +834,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { public VelocityChannelRegistrar getChannelRegistrar() { return channelRegistrar; } - + @Override public boolean isShuttingDown() { return shutdownInProgress.get(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/SuggestionsProvider.java b/proxy/src/main/java/com/velocitypowered/proxy/command/SuggestionsProvider.java index 93088cf6..9c65c023 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/SuggestionsProvider.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/SuggestionsProvider.java @@ -344,7 +344,7 @@ final class SuggestionsProvider { return 0; }); } - return potentials.get(0); + return potentials.getFirst(); } return new ParseResults<>(contextSoFar, originalReader, Collections.emptyMap()); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java index 095445bf..7c04a76d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -140,7 +140,7 @@ public class VelocityCommandManager implements CommandManager { command + " implements multiple registrable Command subinterfaces: " + implementedInterfaces); } else { - this.internalRegister(commandRegistrars.get(0), command, meta); + this.internalRegister(commandRegistrars.getFirst(), command, meta); } } @@ -256,7 +256,7 @@ public class VelocityCommandManager implements CommandManager { } } catch (final Throwable e) { // Ugly, ugly swallowing of everything Throwable, because plugins are naughty. - throw new RuntimeException("Unable to invoke command " + parsed.getReader().getString() + "for " + source, e); + throw new RuntimeException("Unable to invoke command " + parsed.getReader().getString() + " for " + source, e); } finally { eventManager.fireAndForget(new PostCommandInvocationEvent(source, parsed.getReader().getString(), result)); } @@ -400,4 +400,4 @@ public class VelocityCommandManager implements CommandManager { return MoreExecutors.directExecutor(); } } -} \ No newline at end of file +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java index 3c2ad199..1e4a01ce 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java @@ -70,33 +70,34 @@ public final class VelocityCommands { maybeCommand = VelocityBrigadierCommandWrapper.wrap(delegate.getCommand(), registrant); } - if (delegate instanceof LiteralCommandNode lcn) { - var literalBuilder = shallowCopyAsBuilder(lcn, delegate.getName(), true); - literalBuilder.executes(maybeCommand); - // we also need to wrap any children - for (final CommandNode child : delegate.getChildren()) { - literalBuilder.then(wrap(child, registrant)); + return switch (delegate) { + case LiteralCommandNode lcn -> { + var literalBuilder = shallowCopyAsBuilder(lcn, delegate.getName(), true); + literalBuilder.executes(maybeCommand); + // we also need to wrap any children + for (final CommandNode child : delegate.getChildren()) { + literalBuilder.then(wrap(child, registrant)); + } + if (delegate.getRedirect() != null) { + literalBuilder.redirect(wrap(delegate.getRedirect(), registrant)); + } + yield literalBuilder.build(); } - if (delegate.getRedirect() != null) { - literalBuilder.redirect(wrap(delegate.getRedirect(), registrant)); + case VelocityArgumentCommandNode vacn -> vacn.withCommand(maybeCommand) + .withRedirect(delegate.getRedirect() != null ? wrap(delegate.getRedirect(), registrant) : null); + case ArgumentCommandNode node -> { + var argBuilder = node.createBuilder().executes(maybeCommand); + // we also need to wrap any children + for (final CommandNode child : delegate.getChildren()) { + argBuilder.then(wrap(child, registrant)); + } + if (delegate.getRedirect() != null) { + argBuilder.redirect(wrap(delegate.getRedirect(), registrant)); + } + yield argBuilder.build(); } - return literalBuilder.build(); - } else if (delegate instanceof VelocityArgumentCommandNode vacn) { - return vacn.withCommand(maybeCommand) - .withRedirect(delegate.getRedirect() != null ? wrap(delegate.getRedirect(), registrant) : null); - } else if (delegate instanceof ArgumentCommandNode) { - var argBuilder = delegate.createBuilder().executes(maybeCommand); - // we also need to wrap any children - for (final CommandNode child : delegate.getChildren()) { - argBuilder.then(wrap(child, registrant)); - } - if (delegate.getRedirect() != null) { - argBuilder.redirect(wrap(delegate.getRedirect(), registrant)); - } - return argBuilder.build(); - } else { - throw new IllegalArgumentException("Unsupported node type: " + delegate.getClass()); - } + default -> throw new IllegalArgumentException("Unsupported node type: " + delegate.getClass()); + }; } // Normalization @@ -133,7 +134,7 @@ public final class VelocityCommands { if (nodes.isEmpty()) { throw new IllegalArgumentException("Cannot read alias from empty node list"); } - return nodes.get(0).getNode().getName(); + return nodes.getFirst().getNode().getName(); } public static final String ARGS_NODE_NAME = "arguments"; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNode.java b/proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNode.java index 03ba6d35..e5382d00 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNode.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNode.java @@ -118,14 +118,12 @@ public class VelocityArgumentCommandNode extends ArgumentCommandNode that = (VelocityArgumentCommandNode) o; return this.type.equals(that.type); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java index 2b73dcc1..ea43182e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java @@ -38,6 +38,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.translation.Argument; /** * Implements the Velocity default {@code /glist} command. @@ -111,7 +112,7 @@ public class GlistCommand { if (registeredServer.isEmpty()) { source.sendMessage( CommandMessages.SERVER_DOES_NOT_EXIST - .arguments(Component.text(serverName))); + .arguments(Argument.string("server", serverName))); return -1; } sendServerPlayers(source, registeredServer.get(), false); @@ -126,7 +127,8 @@ public class GlistCommand { ? "velocity.command.glist-player-singular" : "velocity.command.glist-player-plural" ).color(NamedTextColor.YELLOW) - .arguments(Component.text(Integer.toString(online), NamedTextColor.GREEN)); + .arguments(Argument.component( + "players", Component.text(Integer.toString(online), NamedTextColor.GREEN))); target.sendMessage(msg.build()); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/SendCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/SendCommand.java index d0df0348..aeeca118 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/SendCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/SendCommand.java @@ -35,6 +35,7 @@ import java.util.Objects; import java.util.Optional; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.translation.Argument; /** * Implements the Velocity default {@code /send} command. @@ -121,7 +122,7 @@ public class SendCommand { if (maybeServer.isEmpty()) { context.getSource().sendMessage( - CommandMessages.SERVER_DOES_NOT_EXIST.arguments(Component.text(serverName)) + CommandMessages.SERVER_DOES_NOT_EXIST.arguments(Argument.string("server", serverName)) ); return 0; } @@ -133,7 +134,7 @@ public class SendCommand { && !Objects.equals(player, "all") && !Objects.equals(player, "current")) { context.getSource().sendMessage( - CommandMessages.PLAYER_NOT_FOUND.arguments(Component.text(player)) + CommandMessages.PLAYER_NOT_FOUND.arguments(Argument.string("player", player)) ); return 0; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ServerCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ServerCommand.java index 4ec40070..99fd348d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ServerCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ServerCommand.java @@ -37,6 +37,7 @@ import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.translation.Argument; /** * Implements Velocity's {@code /server} command. @@ -76,7 +77,7 @@ public final class ServerCommand { final Optional toConnect = server.getServer(serverName); if (toConnect.isEmpty()) { player.sendMessage(CommandMessages.SERVER_DOES_NOT_EXIST - .arguments(Component.text(serverName))); + .arguments(Argument.string("server", serverName))); return -1; } @@ -135,7 +136,7 @@ public final class ServerCommand { } else { playersTextComponent.key("velocity.command.server-tooltip-players-online"); } - playersTextComponent.arguments(Component.text(connectedPlayers)); + playersTextComponent.arguments(Argument.component("players", Component.text(connectedPlayers))); if (serverInfo.getName().equals(currentPlayerServer)) { serverTextComponent.color(NamedTextColor.GREEN) .hoverEvent( diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java index 6b233b87..562efee4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java @@ -62,6 +62,7 @@ import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.minimessage.translation.Argument; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -165,9 +166,9 @@ public final class VelocityCommand { .build(); final Component copyright = Component .translatable("velocity.command.version-copyright", - Component.text(version.getVendor()), - Component.text(version.getName()), - Component.text(LocalDate.now().getYear())); + Argument.string("vendor", version.getVendor()), + Argument.string("name", version.getName()), + Argument.component("year", Component.text(LocalDate.now().getYear()))); source.sendMessage(velocity); source.sendMessage(copyright); @@ -220,7 +221,7 @@ public final class VelocityCommand { final TranslatableComponent output = Component.translatable() .key("velocity.command.plugins-list") .color(NamedTextColor.YELLOW) - .arguments(listBuilder.build()) + .arguments(Argument.component("plugins", listBuilder.build())) .build(); source.sendMessage(output); return Command.SINGLE_SUCCESS; @@ -236,17 +237,17 @@ public final class VelocityCommand { hoverText.append(Component.newline()); hoverText.append(Component.translatable( "velocity.command.plugin-tooltip-website", - Component.text(url))); + Argument.component("url", Component.text(url)))); }); if (!description.getAuthors().isEmpty()) { hoverText.append(Component.newline()); if (description.getAuthors().size() == 1) { hoverText.append(Component.translatable("velocity.command.plugin-tooltip-author", - Component.text(description.getAuthors().get(0)))); + Component.text(description.getAuthors().getFirst()))); } else { hoverText.append( Component.translatable("velocity.command.plugin-tooltip-author", - Component.text(String.join(", ", description.getAuthors())) + Argument.string("authors", String.join(", ", description.getAuthors())) ) ); } 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 6f0fb61c..e198a071 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -29,6 +29,7 @@ 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.MiniMessageTranslationsMigration; import com.velocitypowered.proxy.config.migration.MotdMigration; import com.velocitypowered.proxy.config.migration.TransferIntegrationMigration; import com.velocitypowered.proxy.util.AddressUtil; @@ -93,6 +94,8 @@ public class VelocityConfiguration implements ProxyConfig { private @Nullable Favicon favicon; @Expose private boolean forceKeyAuthentication = true; // Added in 1.19 + @Expose + private PacketLimiterConfig packetLimiterConfig = PacketLimiterConfig.DEFAULT; private VelocityConfiguration(Servers servers, ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics) { @@ -109,7 +112,7 @@ public class VelocityConfiguration implements ProxyConfig { boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough, boolean samplePlayersInPing, boolean enablePlayerAddressLogging, Servers servers, ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics, - boolean forceKeyAuthentication) { + boolean forceKeyAuthentication, PacketLimiterConfig packetLimiterConfig) { this.bind = bind; this.motd = motd; this.showMaxPlayers = showMaxPlayers; @@ -128,6 +131,7 @@ public class VelocityConfiguration implements ProxyConfig { this.query = query; this.metrics = metrics; this.forceKeyAuthentication = forceKeyAuthentication; + this.packetLimiterConfig = packetLimiterConfig; } /** @@ -156,19 +160,16 @@ public class VelocityConfiguration implements ProxyConfig { } switch (playerInfoForwardingMode) { - case NONE: - logger.warn("Player info forwarding is disabled! All players will appear to be connecting " + case NONE -> logger.warn("Player info forwarding is disabled! All players will appear to be connecting " + "from the proxy and will have offline-mode UUIDs."); - break; - case MODERN: - case BUNGEEGUARD: + case MODERN, BUNGEEGUARD -> { if (forwardingSecret == null || forwardingSecret.length == 0) { logger.error("You don't have a forwarding secret set. This is required for security."); valid = false; } - break; - default: - break; + } + default -> { + } } if (servers.getServers().isEmpty()) { @@ -449,6 +450,10 @@ public class VelocityConfiguration implements ProxyConfig { return advanced.isEnableReusePort(); } + public PacketLimiterConfig getPacketLimiterConfig() { + return packetLimiterConfig; + } + @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -466,6 +471,7 @@ public class VelocityConfiguration implements ProxyConfig { .add("favicon", favicon) .add("enablePlayerAddressLogging", enablePlayerAddressLogging) .add("forceKeyAuthentication", forceKeyAuthentication) + .add("packetLimiterConfig", packetLimiterConfig) .toString(); } @@ -504,6 +510,7 @@ public class VelocityConfiguration implements ProxyConfig { new ForwardingMigration(), new KeyAuthenticationMigration(), new MotdMigration(), + new MiniMessageTranslationsMigration(), new TransferIntegrationMigration() }; @@ -561,6 +568,7 @@ public class VelocityConfiguration implements ProxyConfig { final boolean kickExisting = config.getOrElse("kick-existing-players", false); final boolean enablePlayerAddressLogging = config.getOrElse( "enable-player-address-logging", true); + final PacketLimiterConfig packetLimiterConfig = PacketLimiterConfig.fromConfig(config.get("packet-limiter")); // Throw an exception if the forwarding-secret file is empty and the proxy is using a // forwarding mode that requires it. @@ -588,7 +596,8 @@ public class VelocityConfiguration implements ProxyConfig { new Advanced(advancedConfig), new Query(queryConfig), new Metrics(metricsConfig), - forceKeyAuthentication + forceKeyAuthentication, + packetLimiterConfig ); } } @@ -991,4 +1000,33 @@ public class VelocityConfiguration implements ProxyConfig { return enabled; } } + + /** + * Configuration for packet limiting. + * + * @param interval the interval in seconds to measure packets over + * @param pps the maximum number of packets per second allowed + * @param bytes the maximum number of bytes per second allowed + */ + public record PacketLimiterConfig(int interval, int pps, int bytes) { + public static PacketLimiterConfig DEFAULT = new PacketLimiterConfig(7, 500, -1); + + /** + * returns a PacketLimiterConfig from a config section, or the default if the section is null. + * + * @param config the configuration object to parse + * @return the packet limiter config, or the default if {@code config} is null + */ + public static PacketLimiterConfig fromConfig(CommentedConfig config) { + if (config != null) { + return new PacketLimiterConfig( + config.getIntOrElse("interval", DEFAULT.interval()), + config.getIntOrElse("packets-per-second", DEFAULT.pps()), + config.getIntOrElse("bytes-per-second", DEFAULT.bytes()) + ); + } else { + return DEFAULT; + } + } + } } 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 a7fbb89f..d28578a6 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 @@ -28,6 +28,7 @@ public sealed interface ConfigurationMigration permits ForwardingMigration, KeyAuthenticationMigration, MotdMigration, + MiniMessageTranslationsMigration, TransferIntegrationMigration { boolean shouldMigrate(CommentedFileConfig config); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/migration/MiniMessageTranslationsMigration.java b/proxy/src/main/java/com/velocitypowered/proxy/config/migration/MiniMessageTranslationsMigration.java new file mode 100644 index 00000000..aff5424a --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/migration/MiniMessageTranslationsMigration.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ + +package com.velocitypowered.proxy.config.migration; + +import com.electronwill.nightconfig.core.file.CommentedFileConfig; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Pattern; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.apache.logging.log4j.Logger; + +/** + * Migration from old to modern language argument format with MiniMessage. + * Also migrates possible use of legacy colors to MiniMessage format. + */ +public final class MiniMessageTranslationsMigration implements ConfigurationMigration { + @Override + public boolean shouldMigrate(final CommentedFileConfig config) { + // Checking whether translations should be migrated would be just as costly as attempting to migrate them directly. + return true; + } + + @Override + public void migrate(final CommentedFileConfig config, final Logger logger) throws IOException { + final Path langFolder = Path.of("lang"); + if (Files.notExists(langFolder)) { + return; + } + final Pattern oldPlaceholderPattern = Pattern.compile("\\{(\\d+)}"); + try (final DirectoryStream stream + = Files.newDirectoryStream(langFolder, Files::isRegularFile)) { + for (final Path path : stream) { + String content = Files.readString(path, StandardCharsets.UTF_8); + if (content.indexOf('{') == -1) { + continue; + } + // Migrate old arguments + content = oldPlaceholderPattern.matcher(content).replaceAll(""); + // Some setups use legacy color codes, this format is migrated to MiniMessage + content = MiniMessage.miniMessage().serialize( + LegacyComponentSerializer.legacySection().deserialize(content)); + Files.writeString(path, content, StandardCharsets.UTF_8); + } + } + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java index 3d7ed448..0071716d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java @@ -33,6 +33,7 @@ import com.velocitypowered.natives.encryption.VelocityCipher; import com.velocitypowered.natives.encryption.VelocityCipherFactory; import com.velocitypowered.natives.util.Natives; import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler; import com.velocitypowered.proxy.connection.client.InitialLoginSessionHandler; import com.velocitypowered.proxy.connection.client.StatusSessionHandler; @@ -66,7 +67,7 @@ import io.netty.util.ReferenceCountUtil; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.security.GeneralSecurityException; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -108,7 +109,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { this.server = server; this.state = StateRegistry.HANDSHAKE; - this.sessionHandlers = new HashMap<>(); + this.sessionHandlers = new EnumMap<>(StateRegistry.class); } @Override @@ -153,13 +154,13 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { if (msg instanceof MinecraftPacket pkt) { if (!pkt.handle(activeSessionHandler)) { - activeSessionHandler.handleGeneric((MinecraftPacket) msg); + activeSessionHandler.handleGeneric(pkt); } } else if (msg instanceof HAProxyMessage proxyMessage) { this.remoteAddress = new InetSocketAddress(proxyMessage.sourceAddress(), proxyMessage.sourcePort()); - } else if (msg instanceof ByteBuf) { - activeSessionHandler.handleUnknown((ByteBuf) msg); + } else if (msg instanceof ByteBuf buf) { + activeSessionHandler.handleUnknown(buf); } } finally { ReferenceCountUtil.release(msg); @@ -368,6 +369,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { public void setState(StateRegistry state) { ensureInEventLoop(); + final StateRegistry previousState = this.state; this.state = state; final MinecraftVarintFrameDecoder frameDecoder = this.channel.pipeline() .get(MinecraftVarintFrameDecoder.class); @@ -388,7 +390,13 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { if (state == StateRegistry.CONFIG) { // Activate the play packet queue - addPlayPacketQueueHandler(); + if (previousState == StateRegistry.PLAY + && this.pendingConfigurationSwitch + && this.association instanceof ConnectedPlayer) { + addPlayPacketQueueOutboundHandler(); + } else { + addPlayPacketQueueHandler(); + } } else { // Remove the queue if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_OUTBOUND) != null) { @@ -404,13 +412,23 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { * Adds the play packet queue handler. */ public void addPlayPacketQueueHandler() { - if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_OUTBOUND) == null) { - this.channel.pipeline().addAfter(Connections.MINECRAFT_ENCODER, Connections.PLAY_PACKET_QUEUE_OUTBOUND, - new PlayPacketQueueOutboundHandler(this.protocolVersion, channel.pipeline().get(MinecraftEncoder.class).getDirection())); - } + addPlayPacketQueueOutboundHandler(); + if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_INBOUND) == null) { this.channel.pipeline().addAfter(Connections.MINECRAFT_DECODER, Connections.PLAY_PACKET_QUEUE_INBOUND, - new PlayPacketQueueInboundHandler(this.protocolVersion, channel.pipeline().get(MinecraftDecoder.class).getDirection())); + new PlayPacketQueueInboundHandler(this.protocolVersion, + channel.pipeline().get(MinecraftDecoder.class).getDirection())); + } + } + + /** + * Adds only the outbound play packet queue handler. + */ + public void addPlayPacketQueueOutboundHandler() { + if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_OUTBOUND) == null) { + this.channel.pipeline().addAfter(Connections.MINECRAFT_ENCODER, Connections.PLAY_PACKET_QUEUE_OUTBOUND, + new PlayPacketQueueOutboundHandler(this.protocolVersion, + channel.pipeline().get(MinecraftEncoder.class).getDirection())); } } @@ -544,9 +562,10 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { } else { int level = server.getConfiguration().getCompressionLevel(); VelocityCompressor compressor = Natives.compress.get().create(level); + final MinecraftDecoder minecraftDecoder = (MinecraftDecoder) channel.pipeline().get(MINECRAFT_DECODER); encoder = new MinecraftCompressorAndLengthEncoder(threshold, compressor); - decoder = new MinecraftCompressDecoder(threshold, compressor); + decoder = new MinecraftCompressDecoder(threshold, compressor, minecraftDecoder.getDirection()); channel.pipeline().remove(FRAME_ENCODER); channel.pipeline().addBefore(MINECRAFT_DECODER, COMPRESSION_DECODER, decoder); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java index 7fda94bd..f44859bd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java @@ -70,6 +70,7 @@ import com.velocitypowered.proxy.protocol.packet.TransferPacket; import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket; +import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; @@ -93,6 +94,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { Boolean.getBoolean("velocity.log-server-backpressure"); private static final int MAXIMUM_PACKETS_TO_FLUSH = Integer.getInteger("velocity.max-packets-per-flush", 8192); + private static final int LARGE_PACKET_THRESHOLD = 1024 * 128; private final VelocityServer server; private final VelocityServerConnection serverConn; @@ -437,11 +439,12 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public void handleGeneric(MinecraftPacket packet) { - if (packet instanceof PluginMessagePacket) { - ((PluginMessagePacket) packet).retain(); + if (packet instanceof PluginMessagePacket pluginMessage) { + pluginMessage.retain(); } + boolean huge = packet instanceof DeferredByteBufHolder def && def.content().readableBytes() > LARGE_PACKET_THRESHOLD; playerConnection.delayedWrite(packet); - if (++packetsFlushed >= MAXIMUM_PACKETS_TO_FLUSH) { + if (huge || ++packetsFlushed >= MAXIMUM_PACKETS_TO_FLUSH) { playerConnection.flush(); packetsFlushed = 0; } @@ -449,8 +452,9 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public void handleUnknown(ByteBuf buf) { + boolean huge = buf.readableBytes() > LARGE_PACKET_THRESHOLD; playerConnection.delayedWrite(buf.retain()); - if (++packetsFlushed >= MAXIMUM_PACKETS_TO_FLUSH) { + if (huge || ++packetsFlushed >= MAXIMUM_PACKETS_TO_FLUSH) { playerConnection.flush(); packetsFlushed = 0; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java index a2ea94d6..50edfaee 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java @@ -344,66 +344,30 @@ public class BungeeCordMessageResponder { return false; } - ByteBufDataInput in = new ByteBufDataInput(message.content()); - String subChannel = in.readUTF(); + final ByteBufDataInput in = new ByteBufDataInput(message.content()); + final String subChannel = in.readUTF(); switch (subChannel) { - case "GetPlayerServer": - this.processGetPlayerServer(in); - break; - case "ForwardToPlayer": - this.processForwardToPlayer(in); - break; - case "Forward": - this.processForwardToServer(in); - break; - case "Connect": - this.processConnect(in); - break; - case "ConnectOther": - this.processConnectOther(in); - break; - case "IP": - this.processIp(in); - break; - case "PlayerCount": - this.processPlayerCount(in); - break; - case "PlayerList": - this.processPlayerList(in); - break; - case "GetServers": - this.processGetServers(); - break; - case "Message": - this.processMessage(in); - break; - case "MessageRaw": - this.processMessageRaw(in); - break; - case "GetServer": - this.processGetServer(); - break; - case "UUID": - this.processUuid(); - break; - case "UUIDOther": - this.processUuidOther(in); - break; - case "IPOther": - this.processIpOther(in); - break; - case "ServerIP": - this.processServerIp(in); - break; - case "KickPlayer": - this.processKick(in); - break; - case "KickPlayerRaw": - this.processKickRaw(in); - break; - default: - // Do nothing, unknown command - break; + case "GetPlayerServer" -> this.processGetPlayerServer(in); + case "ForwardToPlayer" -> this.processForwardToPlayer(in); + case "Forward" -> this.processForwardToServer(in); + case "Connect" -> this.processConnect(in); + case "ConnectOther" -> this.processConnectOther(in); + case "IP" -> this.processIp(in); + case "PlayerCount" -> this.processPlayerCount(in); + case "PlayerList" -> this.processPlayerList(in); + case "GetServers" -> this.processGetServers(); + case "Message" -> this.processMessage(in); + case "MessageRaw" -> this.processMessageRaw(in); + case "GetServer" -> this.processGetServer(); + case "UUID" -> this.processUuid(); + case "UUIDOther" -> this.processUuidOther(in); + case "IPOther" -> this.processIpOther(in); + case "ServerIP" -> this.processServerIp(in); + case "KickPlayer" -> this.processKick(in); + case "KickPlayerRaw" -> this.processKickRaw(in); + default -> { + // Do nothing, unknown command + } } return true; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java index 8ab38e58..8cc1bf8c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java @@ -60,7 +60,7 @@ import com.velocitypowered.proxy.protocol.packet.config.TagsUpdatePacket; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; -import java.io.IOException; +import io.netty.channel.Channel; import java.net.InetSocketAddress; import java.util.concurrent.CompletableFuture; import net.kyori.adventure.key.Key; @@ -72,6 +72,9 @@ import org.apache.logging.log4j.Logger; * 1.20.2+ switching. Yes, some of this is exceptionally stupid. */ public class ConfigSessionHandler implements MinecraftSessionHandler { + private static final boolean BACKPRESSURE_LOG = + Boolean.getBoolean("velocity.log-server-backpressure"); + private static final Logger logger = LogManager.getLogger(ConfigSessionHandler.class); private final VelocityServer server; private final VelocityServerConnection serverConn; @@ -373,8 +376,8 @@ public class ConfigSessionHandler implements MinecraftSessionHandler { @Override public void disconnected() { - resultFuture.completeExceptionally( - new IOException("Unexpectedly disconnected from remote server")); + resultFuture.complete(ConnectionRequestResults.forDisconnect( + ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR, serverConn.getServer())); } @Override @@ -382,6 +385,22 @@ public class ConfigSessionHandler implements MinecraftSessionHandler { serverConn.getPlayer().getConnection().write(packet); } + @Override + public void writabilityChanged() { + Channel serverChan = serverConn.ensureConnected().getChannel(); + boolean writable = serverChan.isWritable(); + + if (BACKPRESSURE_LOG) { + if (writable) { + logger.info("{} is writable, will auto-read player connection data", this.serverConn); + } else { + logger.info("{} is not writable, not auto-reading player connection data", this.serverConn); + } + } + + serverConn.getPlayer().getConnection().setAutoReading(writable); + } + private void switchFailure(Throwable cause) { logger.error("Unable to switch to new server {} for {}", serverConn.getServerInfo().getName(), serverConn.getPlayer().getUsername(), cause); @@ -395,4 +414,4 @@ public class ConfigSessionHandler implements MinecraftSessionHandler { public enum State { START, NEGOTIATING, PLUGIN_MESSAGE_INTERRUPT, RESOURCE_PACK_INTERRUPT, COMPLETE } -} \ No newline at end of file +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java index a74f7c56..cfcd8f5e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java @@ -38,7 +38,6 @@ import com.velocitypowered.proxy.protocol.packet.DisconnectPacket; import com.velocitypowered.proxy.protocol.packet.JoinGamePacket; import com.velocitypowered.proxy.protocol.packet.KeepAlivePacket; import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket; -import java.io.IOException; import java.util.concurrent.CompletableFuture; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -122,9 +121,8 @@ public class TransitionSessionHandler implements MinecraftSessionHandler { // Change the client to use the ClientPlaySessionHandler if required. ClientPlaySessionHandler playHandler; if (player.getConnection() - .getActiveSessionHandler() instanceof ClientPlaySessionHandler) { - playHandler = - (ClientPlaySessionHandler) player.getConnection().getActiveSessionHandler(); + .getActiveSessionHandler() instanceof ClientPlaySessionHandler sessionHandler) { + playHandler = sessionHandler; } else { playHandler = new ClientPlaySessionHandler(server, player); player.getConnection().setActiveSessionHandler(StateRegistry.PLAY, playHandler); @@ -214,7 +212,7 @@ public class TransitionSessionHandler implements MinecraftSessionHandler { @Override public void disconnected() { - resultFuture - .completeExceptionally(new IOException("Unexpectedly disconnected from remote server")); + resultFuture.complete(ConnectionRequestResults.forDisconnect( + ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR, serverConn.getServer())); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java index 71ebd7bc..c40a7aaa 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java @@ -180,9 +180,8 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, handshake.setServerAddress(createBungeeGuardForwardingAddress(secret)); } else if (proxyPlayer.getConnection().getType() == ConnectionTypes.LEGACY_FORGE) { handshake.setServerAddress(playerVhost + HANDSHAKE_HOSTNAME_TOKEN); - } else if (proxyPlayer.getConnection().getType() instanceof ModernForgeConnectionType) { - handshake.setServerAddress(playerVhost + ((ModernForgeConnectionType) proxyPlayer - .getConnection().getType()).getModernToken()); + } else if (proxyPlayer.getConnection().getType() instanceof ModernForgeConnectionType forgeConnection) { + handshake.setServerAddress(playerVhost + forgeConnection.getModernToken()); } else { handshake.setServerAddress(playerVhost); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java index 0aea8d77..605388ea 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java @@ -69,14 +69,16 @@ public class AuthSessionHandler implements MinecraftSessionHandler { private @MonotonicNonNull ConnectedPlayer connectedPlayer; private final boolean onlineMode; private State loginState = State.START; // 1.20.2+ + private final String serverIdHash; AuthSessionHandler(VelocityServer server, LoginInboundConnection inbound, - GameProfile profile, boolean onlineMode) { + GameProfile profile, boolean onlineMode, String serverIdHash) { this.server = Preconditions.checkNotNull(server, "server"); this.inbound = Preconditions.checkNotNull(inbound, "inbound"); this.profile = Preconditions.checkNotNull(profile, "profile"); this.onlineMode = onlineMode; this.mcConnection = inbound.delegatedConnection(); + this.serverIdHash = serverIdHash; } @Override @@ -213,7 +215,7 @@ public class AuthSessionHandler implements MinecraftSessionHandler { private void completeLoginProtocolPhaseAndInitialize(ConnectedPlayer player) { mcConnection.setAssociation(player); - server.getEventManager().fire(new LoginEvent(player)).thenAcceptAsync(event -> { + server.getEventManager().fire(new LoginEvent(player, serverIdHash)).thenAcceptAsync(event -> { if (mcConnection.isClosed()) { // The player was disconnected server.getEventManager().fireAndForget(new DisconnectEvent(player, diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java index 776f99d6..e4c754f9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java @@ -33,6 +33,7 @@ import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResp import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; import com.velocitypowered.proxy.protocol.packet.ClientSettingsPacket; import com.velocitypowered.proxy.protocol.packet.KeepAlivePacket; @@ -60,6 +61,8 @@ import org.apache.logging.log4j.Logger; * Handles the client config stage. */ public class ClientConfigSessionHandler implements MinecraftSessionHandler { + private static final boolean BACKPRESSURE_LOG = + Boolean.getBoolean("velocity.log-server-backpressure"); private static final Logger logger = LogManager.getLogger(ClientConfigSessionHandler.class); private final VelocityServer server; @@ -266,6 +269,36 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler { @Override public void exception(Throwable throwable) { player.disconnect(Component.translatable("velocity.error.player-connection-error", NamedTextColor.RED)); + if (MinecraftDecoder.DEBUG) { + logger.info("Exception while handling packet for {}", player, throwable); + } + } + + @Override + public void writabilityChanged() { + final boolean writable = player.getConnection().getChannel().isWritable(); + + if (BACKPRESSURE_LOG) { + if (writable) { + logger.info("{} is writable, will auto-read backend connection data", player); + } else { + logger.info("{} is not writable, not auto-reading backend connection data", player); + } + } + + if (!writable) { + // Flush pending packets to free up memory. Schedule on a future event loop invocation + // to avoid disabling auto-read while the flush resolves backpressure. + player.getConnection().eventLoop().execute(() -> player.getConnection().flush()); + } + + final VelocityServerConnection serverConn = player.getConnectionInFlightOrConnectedServer(); + if (serverConn != null) { + final MinecraftConnection smc = serverConn.getConnection(); + if (smc != null) { + smc.setAutoReading(writable); + } + } } /** diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index de5f3f01..5a34daff 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -24,6 +24,7 @@ import com.mojang.brigadier.suggestion.Suggestion; import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.player.CookieReceiveEvent; import com.velocitypowered.api.event.player.PlayerChannelRegisterEvent; +import com.velocitypowered.api.event.player.PlayerChannelUnregisterEvent; import com.velocitypowered.api.event.player.PlayerClientBrandEvent; import com.velocitypowered.api.event.player.TabCompleteEvent; import com.velocitypowered.api.event.player.configuration.PlayerEnteredConfigurationEvent; @@ -42,6 +43,7 @@ import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants; import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; import com.velocitypowered.proxy.protocol.packet.BossBarPacket; import com.velocitypowered.proxy.protocol.packet.ClientSettingsPacket; import com.velocitypowered.proxy.protocol.packet.JoinGamePacket; @@ -99,6 +101,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; * center that joins backend servers with players. */ public class ClientPlaySessionHandler implements MinecraftSessionHandler { + private static final boolean BACKPRESSURE_LOG = + Boolean.getBoolean("velocity.log-server-backpressure"); private static final Logger logger = LogManager.getLogger(ClientPlaySessionHandler.class); @@ -320,8 +324,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { new PlayerChannelRegisterEvent(player, ImmutableList.copyOf(channels))); backendConn.write(packet.retain()); } else if (PluginMessageUtil.isUnregister(packet)) { - player.getClientsideChannels() - .removeAll(PluginMessageUtil.getChannels(0, packet, this.player.getProtocolVersion())); + List channels = + PluginMessageUtil.getChannels(0, packet, this.player.getProtocolVersion()); + player.getClientsideChannels().removeAll(channels); + server.getEventManager() + .fireAndForget( + new PlayerChannelUnregisterEvent(player, ImmutableList.copyOf(channels))); backendConn.write(packet.retain()); } else if (PluginMessageUtil.isMcBrand(packet)) { String brand = PluginMessageUtil.readBrandMessage(packet.content()); @@ -447,7 +455,11 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } MinecraftConnection smc = serverConnection.getConnection(); - if (smc != null && serverConnection.getPhase().consideredComplete()) { + final boolean stateAllowsForward = smc != null + && !smc.isClosed() + && serverConnection.getPhase().consideredComplete() + && smc.getState() == StateRegistry.PLAY; + if (stateAllowsForward) { if (packet instanceof PluginMessagePacket) { ((PluginMessagePacket) packet).retain(); } @@ -464,7 +476,11 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } MinecraftConnection smc = serverConnection.getConnection(); - if (smc != null && !smc.isClosed() && serverConnection.getPhase().consideredComplete()) { + final boolean stateAllowsForward = smc != null + && !smc.isClosed() + && serverConnection.getPhase().consideredComplete() + && smc.getState() == StateRegistry.PLAY; + if (stateAllowsForward) { smc.write(buf.retain()); } } @@ -476,14 +492,24 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { @Override public void exception(Throwable throwable) { - player.disconnect( - Component.translatable("velocity.error.player-connection-error", NamedTextColor.RED)); + player.disconnect(Component.translatable("velocity.error.player-connection-error", NamedTextColor.RED)); + if (MinecraftDecoder.DEBUG) { + logger.info("Exception while handling packet for {}", player, throwable); + } } @Override public void writabilityChanged() { boolean writable = player.getConnection().getChannel().isWritable(); + if (BACKPRESSURE_LOG) { + if (writable) { + logger.info("{} is writable, will auto-read backend connection data", player); + } else { + logger.info("{} is not writable, not auto-reading backend connection data", player); + } + } + if (!writable) { // We might have packets queued from the server, so flush them now to free up memory. Make // sure to do it on a future invocation of the event loop, otherwise while the issue will diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index 0807789f..47d59a71 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -135,6 +135,7 @@ import net.kyori.adventure.sound.SoundStop; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.logger.slf4j.ComponentLogger; +import net.kyori.adventure.text.minimessage.translation.Argument; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.kyori.adventure.title.Title.Times; @@ -713,12 +714,12 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, Component friendlyError; if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) { friendlyError = Component.translatable("velocity.error.connected-server-error", - Component.text(server.getServerInfo().getName())); + Argument.string("server", server.getServerInfo().getName())); } else { logger.error("{}: unable to connect to server {}", this, server.getServerInfo().getName(), wrapped); friendlyError = Component.translatable("velocity.error.connecting-server-error", - Component.text(server.getServerInfo().getName())); + Argument.string("server", server.getServerInfo().getName())); } handleConnectionException(server, null, friendlyError.color(NamedTextColor.RED), safe); } @@ -746,7 +747,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, } handleConnectionException(server, disconnectReason, Component.translatable("velocity.error.moved-to-new-server", NamedTextColor.RED, - Component.text(server.getServerInfo().getName()), + Argument.string("server", server.getServerInfo().getName()), disconnectReason), safe); } else { if (this.server.getConfiguration().isLogPlayerConnections()) { @@ -755,7 +756,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, } handleConnectionException(server, disconnectReason, Component.translatable("velocity.error.cant-connect", NamedTextColor.RED, - Component.text(server.getServerInfo().getName()), + Argument.string("server", server.getServerInfo().getName()), disconnectReason), safe); } } @@ -811,61 +812,56 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, return; } - if (event.getResult() instanceof final DisconnectPlayer res) { - disconnect(res.getReasonComponent()); - } else if (event.getResult() instanceof final RedirectPlayer res) { - createConnectionRequest(res.getServer(), previousConnection).connect() - .whenCompleteAsync((status, throwable) -> { - if (throwable != null) { - handleConnectionException(res.getServer(), throwable, true); - return; - } + switch (event.getResult()) { + case DisconnectPlayer res -> disconnect(res.getReasonComponent()); + case RedirectPlayer res -> createConnectionRequest(res.getServer(), previousConnection).connect() + .whenCompleteAsync((status, throwable) -> { + if (throwable != null) { + handleConnectionException(res.getServer(), throwable, true); + return; + } - switch (status.getStatus()) { - // Impossible/nonsensical cases - case ALREADY_CONNECTED: - logger.error("{}: already connected to {}", this, - status.getAttemptedConnection().getServerInfo().getName()); - break; - case CONNECTION_IN_PROGRESS: - // Fatal case - case CONNECTION_CANCELLED: - Component fallbackMsg = res.getMessageComponent(); - if (fallbackMsg == null) { - fallbackMsg = friendlyReason; + switch (status.getStatus()) { + // Impossible/nonsensical cases + case ALREADY_CONNECTED -> logger.error("{}: already connected to {}", this, + status.getAttemptedConnection().getServerInfo().getName()); + case CONNECTION_IN_PROGRESS, CONNECTION_CANCELLED -> { + Component fallbackMsg = res.getMessageComponent(); + if (fallbackMsg == null) { + fallbackMsg = friendlyReason; + } + disconnect(status.getReasonComponent().orElse(fallbackMsg)); + } + case SERVER_DISCONNECTED -> { + Component reason = status.getReasonComponent() + .orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR); + handleConnectionException(res.getServer(), + DisconnectPacket.create(reason, getProtocolVersion(), connection.getState()), + ((Impl) status).isSafe()); + } + case SUCCESS -> { + Component requestedMessage = res.getMessageComponent(); + if (requestedMessage == null) { + requestedMessage = friendlyReason; + } + if (requestedMessage != Component.empty()) { + sendMessage(requestedMessage); + } + } + default -> { + // The only remaining value is successful (no need to do anything!) + } } - disconnect(status.getReasonComponent().orElse(fallbackMsg)); - break; - case SERVER_DISCONNECTED: - Component reason = status.getReasonComponent() - .orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR); - handleConnectionException(res.getServer(), - DisconnectPacket.create(reason, getProtocolVersion(), connection.getState()), - ((Impl) status).isSafe()); - break; - case SUCCESS: - Component requestedMessage = res.getMessageComponent(); - if (requestedMessage == null) { - requestedMessage = friendlyReason; - } - if (requestedMessage != Component.empty()) { - sendMessage(requestedMessage); - } - break; - default: - // The only remaining value is successful (no need to do anything!) - break; - } - }, connection.eventLoop()); - } else if (event.getResult() instanceof final Notify res) { - if (event.kickedDuringServerConnect() && previousConnection != null) { - sendMessage(res.getMessageComponent()); - } else { - disconnect(res.getMessageComponent()); + }, connection.eventLoop()); + case Notify res -> { + if (event.kickedDuringServerConnect() && previousConnection != null) { + sendMessage(res.getMessageComponent()); + } else { + disconnect(res.getMessageComponent()); + } } - } else { // In case someone gets creative, assume we want to disconnect the player. - disconnect(friendlyReason); + default -> disconnect(friendlyReason); } }, connection.eventLoop()); } @@ -1353,11 +1349,17 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, final Long sentTime = serverConnection.getPendingPings().remove(packet.getRandomId()); if (sentTime != null) { final MinecraftConnection smc = serverConnection.getConnection(); - if (smc != null) { + final StateRegistry clientState = connection.getState(); + final boolean stateAllowsForward = smc != null + && !smc.isClosed() + && clientState == smc.getState() + && (clientState == StateRegistry.CONFIG || clientState == StateRegistry.PLAY); + if (stateAllowsForward) { setPing(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - sentTime)); smc.write(packet); - return true; } + // We removed this, and so this is ours + return true; } } return false; @@ -1367,7 +1369,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, * Switches the connection to the client into config state. */ public void switchToConfigState() { - server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, getConnectionInFlightOrConnectedServer())) + final VelocityServerConnection targetServer = getConnectionInFlightOrConnectedServer(); + server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, targetServer)) .completeOnTimeout(null, 5, TimeUnit.SECONDS).thenRunAsync(() -> { // if the connection was closed earlier, there is a risk that the player is no longer connected if (!connection.getChannel().isActive()) { @@ -1382,7 +1385,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, connection.pendingConfigurationSwitch = true; connection.getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.CONFIG); // Make sure we don't send any play packets to the player after update start - connection.addPlayPacketQueueHandler(); + connection.addPlayPacketQueueOutboundHandler(); }, connection.eventLoop()).exceptionally((ex) -> { logger.error("Error switching player connection to config state", ex); return null; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java index 80260d2b..83919b85 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java @@ -127,10 +127,10 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { if (!handshake.getProtocolVersion().isSupported()) { // Bump connection into correct protocol state so that we can send the disconnect packet. connection.setState(StateRegistry.LOGIN); - ic.disconnectQuietly(Component.translatable() - .key("multiplayer.disconnect.outdated_client") - .arguments(Component.text(ProtocolVersion.SUPPORTED_VERSION_STRING)) - .build()); + ic.disconnectQuietly(Component.translatable( + "multiplayer.disconnect.outdated_client", + Component.text(ProtocolVersion.SUPPORTED_VERSION_STRING) + )); return; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java index 00187d34..482fb76e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java @@ -152,7 +152,7 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler { } else { mcConnection.setActiveSessionHandler(StateRegistry.LOGIN, new AuthSessionHandler(server, inbound, - GameProfile.forOfflinePlayer(login.getUsername()), false)); + GameProfile.forOfflinePlayer(login.getUsername()), false, null)); } }); }); @@ -214,6 +214,7 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler { server.getVersion().getName() + "/" + server.getVersion().getVersion()) .uri(URI.create(url)) .build(); + //noinspection resource final HttpClient httpClient = server.createHttpClient(); httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString()) .whenCompleteAsync((response, throwable) -> { @@ -254,7 +255,7 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler { } // All went well, initialize the session. mcConnection.setActiveSessionHandler(StateRegistry.LOGIN, - new AuthSessionHandler(server, inbound, profile, true)); + new AuthSessionHandler(server, inbound, profile, true, serverId)); } else if (response.statusCode() == 204) { // Apparently an offline-mode user logged onto this online-mode proxy. inbound.disconnect( @@ -268,14 +269,12 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler { } }, mcConnection.eventLoop()) .thenRun(() -> { - if (httpClient instanceof final AutoCloseable closeable) { - try { - closeable.close(); - } catch (Exception e) { - // In Java 21, the HttpClient does not throw any Exception - // when trying to clean its resources, so this should not happen - logger.error("An unknown error occurred while trying to close an HttpClient", e); - } + try { + httpClient.close(); + } catch (Exception e) { + // In Java 21, the HttpClient does not throw any Exception + // when trying to clean its resources, so this should not happen + logger.error("An unknown error occurred while trying to close an HttpClient", e); } }); } catch (GeneralSecurityException e) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ModernResourcePackHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ModernResourcePackHandler.java index 077ce701..f0fd5e08 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ModernResourcePackHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ModernResourcePackHandler.java @@ -94,7 +94,7 @@ public final class ModernResourcePackHandler extends ResourcePackHandler { this.outstandingResourcePacks.get(info.getId()); outstandingResourcePacks.add(info); if (outstandingResourcePacks.size() == 1) { - tickResourcePackQueue(outstandingResourcePacks.get(0).getId()); + tickResourcePackQueue(outstandingResourcePacks.getFirst().getId()); } } @@ -111,7 +111,7 @@ public final class ModernResourcePackHandler extends ResourcePackHandler { final List outstandingResourcePacks = this.outstandingResourcePacks.get(uuid); if (!outstandingResourcePacks.isEmpty()) { - sendResourcePackRequestPacket(outstandingResourcePacks.get(0)); + sendResourcePackRequestPacket(outstandingResourcePacks.getFirst()); } } @@ -124,7 +124,7 @@ public final class ModernResourcePackHandler extends ResourcePackHandler { this.outstandingResourcePacks.get(uuid); final boolean peek = bundle.status().isIntermediate(); final ResourcePackInfo queued = outstandingResourcePacks.isEmpty() ? null : - peek ? outstandingResourcePacks.get(0) : outstandingResourcePacks.remove(0); + peek ? outstandingResourcePacks.getFirst() : outstandingResourcePacks.removeFirst(); server.getEventManager() .fire(new PlayerResourcePackStatusEvent(this.player, uuid, bundle.status(), queued)) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java index 7740139c..12c6be2b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java @@ -100,65 +100,60 @@ public class ServerListPingHandler { CompletableFuture> pingResponses = CompletableFutures.successfulAsList(pings, (ex) -> fallback); - switch (mode) { - case ALL: - return pingResponses.thenApply(responses -> { - // Find the first non-fallback - for (ServerPing response : responses) { - if (response == fallback) { - continue; - } - - if (response.getDescriptionComponent() == null) { - return response.asBuilder() - .description(Component.empty()) - .build(); - } - - return response; + return switch (mode) { + case ALL -> pingResponses.thenApply(responses -> { + // Find the first non-fallback + for (ServerPing response : responses) { + if (response == fallback) { + continue; } - return fallback; - }); - case MODS: - return pingResponses.thenApply(responses -> { - // Find the first non-fallback that contains a mod list - for (ServerPing response : responses) { - if (response == fallback) { - continue; - } - Optional modInfo = response.getModinfo(); - if (modInfo.isPresent()) { - return fallback.asBuilder().mods(modInfo.get()).build(); - } - } - return fallback; - }); - case DESCRIPTION: - return pingResponses.thenApply(responses -> { - // Find the first non-fallback. If it includes a modlist, add it too. - for (ServerPing response : responses) { - if (response == fallback) { - continue; - } - if (response.getDescriptionComponent() == null) { - continue; - } - - return new ServerPing( - fallback.getVersion(), - fallback.getPlayers().orElse(null), - response.getDescriptionComponent(), - fallback.getFavicon().orElse(null), - response.getModinfo().orElse(null) - ); + if (response.getDescriptionComponent() == null) { + return response.asBuilder() + .description(Component.empty()) + .build(); } - return fallback; - }); + + return response; + } + return fallback; + }); + case MODS -> pingResponses.thenApply(responses -> { + // Find the first non-fallback that contains a mod list + for (ServerPing response : responses) { + if (response == fallback) { + continue; + } + Optional modInfo = response.getModinfo(); + if (modInfo.isPresent()) { + return fallback.asBuilder().mods(modInfo.get()).build(); + } + } + return fallback; + }); + case DESCRIPTION -> pingResponses.thenApply(responses -> { + // Find the first non-fallback. If it includes a modlist, add it too. + for (ServerPing response : responses) { + if (response == fallback) { + continue; + } + if (response.getDescriptionComponent() == null) { + continue; + } + + return new ServerPing( + fallback.getVersion(), + fallback.getPlayers().orElse(null), + response.getDescriptionComponent(), + fallback.getFavicon().orElse(null), + response.getModinfo().orElse(null) + ); + } + return fallback; + }); // Not possible, but covered for completeness. - default: - return CompletableFuture.completedFuture(fallback); - } + default -> CompletableFuture.completedFuture(fallback); + }; } /** diff --git a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java index 90b6292f..fd7d881d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java @@ -33,6 +33,7 @@ import net.kyori.adventure.permission.PermissionChecker; import net.kyori.adventure.platform.facet.FacetPointers; import net.kyori.adventure.platform.facet.FacetPointers.Type; import net.kyori.adventure.pointer.Pointers; +import net.kyori.adventure.pointer.PointersSupplier; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.logger.slf4j.ComponentLogger; @@ -59,11 +60,11 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons private final VelocityServer server; private PermissionFunction permissionFunction = ALWAYS_TRUE; - private final @NotNull Pointers pointers = ConsoleCommandSource.super.pointers().toBuilder() - .withDynamic(PermissionChecker.POINTER, this::getPermissionChecker) - .withDynamic(Identity.LOCALE, () -> ClosestLocaleMatcher.INSTANCE + private static final @NotNull PointersSupplier POINTERS = PointersSupplier.builder() + .resolving(PermissionChecker.POINTER, VelocityConsole::getPermissionChecker) + .resolving(Identity.LOCALE, (console) -> ClosestLocaleMatcher.INSTANCE .lookupClosest(Locale.getDefault())) - .withStatic(FacetPointers.TYPE, Type.CONSOLE) + .resolving(FacetPointers.TYPE, (console) -> Type.CONSOLE) .build(); public VelocityConsole(VelocityServer server) { @@ -153,6 +154,6 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons @Override public @NotNull Pointers pointers() { - return pointers; + return POINTERS.view(this); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java index 0c22dcce..fae9113f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java @@ -26,8 +26,10 @@ import static com.velocitypowered.proxy.network.Connections.MINECRAFT_ENCODER; import static com.velocitypowered.proxy.network.Connections.READ_TIMEOUT; import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.config.VelocityConfiguration; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler; +import com.velocitypowered.proxy.network.limiter.SimpleBytesPerSecondLimiter; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.netty.LegacyPingDecoder; @@ -72,6 +74,17 @@ public class ServerChannelInitializer extends ChannelInitializer { new HandshakeSessionHandler(connection, this.server)); ch.pipeline().addLast(Connections.HANDLER, connection); + VelocityConfiguration.PacketLimiterConfig packetLimiterConfig = + server.getConfiguration().getPacketLimiterConfig(); + int configuredInterval = packetLimiterConfig.interval(); + int configuredPacketsPerSecond = packetLimiterConfig.pps(); + int configuredBytes = packetLimiterConfig.bytes(); + + if (configuredInterval > 0 && (configuredBytes > 0 || configuredPacketsPerSecond > 0)) { + ch.pipeline().get(MinecraftVarintFrameDecoder.class).setPacketLimiter( + new SimpleBytesPerSecondLimiter(configuredPacketsPerSecond, configuredBytes, configuredInterval) + ); + } if (this.server.getConfiguration().isProxyProtocol()) { ch.pipeline().addFirst(new HAProxyMessageDecoder()); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/limiter/PacketLimiter.java b/proxy/src/main/java/com/velocitypowered/proxy/network/limiter/PacketLimiter.java new file mode 100644 index 00000000..e63a0c8b --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/limiter/PacketLimiter.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2025 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.network.limiter; + +/** + * PacketLimiter enforces a limit on the number of bytes processed over a time window. + * Implementations should be thread-safe. + */ +public interface PacketLimiter { + /** + * Attempts to record the specified number of bytes within the current window. + * + * @param bytes the number of bytes to record + * @return true if the bytes are allowed and recorded; false if the limit would be exceeded + */ + boolean account(int bytes); +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/limiter/SimpleBytesPerSecondLimiter.java b/proxy/src/main/java/com/velocitypowered/proxy/network/limiter/SimpleBytesPerSecondLimiter.java new file mode 100644 index 00000000..1c6701be --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/limiter/SimpleBytesPerSecondLimiter.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2025 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.network.limiter; + +import com.velocitypowered.proxy.util.IntervalledCounter; +import org.jspecify.annotations.Nullable; + +/** + * A moving-window limiter over a configurable number of seconds. + * It enforces both packets-per-second and average bytes-per-second limits. + * The effective cap over the full window equals limitPerSecond * windowSeconds. + */ +public final class SimpleBytesPerSecondLimiter implements PacketLimiter { + @Nullable + private final IntervalledCounter bytesCounter; + @Nullable + private final IntervalledCounter packetsCounter; + private final int packetsPerSecond; + private final int bytesPerSecond; + + /** + * Creates a new SimpleBytesPerSecondLimiter. + * + * @param packetsPerSecond maximum average packets per second allowed (> 0) + * @param bytesPerSecond maximum average bytes per second allowed (> 0) + * @param windowSeconds number of seconds in the moving window (> 0) + */ + public SimpleBytesPerSecondLimiter(int packetsPerSecond, int bytesPerSecond, int windowSeconds) { + this.packetsPerSecond = packetsPerSecond; + if (windowSeconds <= 0) { + throw new IllegalArgumentException("windowSeconds must be > 0"); + } + this.bytesPerSecond = bytesPerSecond; + this.packetsCounter = packetsPerSecond > 0 ? new IntervalledCounter((long) (windowSeconds * 1.0e9)) : null; + this.bytesCounter = bytesPerSecond > 0 ? new IntervalledCounter((long) (windowSeconds * 1.0e9)) : null; + + } + + /** + * Records the given payload length as one packet and returns whether it is allowed. + */ + @SuppressWarnings("RedundantIfStatement") + @Override + public boolean account(int bytes) { + long currTime = System.nanoTime(); + if (packetsCounter != null) { + packetsCounter.updateAndAdd(1, currTime); + if (packetsCounter.getRate() > packetsPerSecond) { + return false; + } + } + + if (bytesCounter != null) { + bytesCounter.updateAndAdd(bytes, currTime); + if (bytesCounter.getRate() > bytesPerSecond) { + return false; + } + } + + return true; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/netty/SeparatePoolInetNameResolver.java b/proxy/src/main/java/com/velocitypowered/proxy/network/netty/SeparatePoolInetNameResolver.java index 2997d840..5fc309ae 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/netty/SeparatePoolInetNameResolver.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/netty/SeparatePoolInetNameResolver.java @@ -71,7 +71,7 @@ public final class SeparatePoolInetNameResolver extends InetNameResolver { protected void doResolve(String inetHost, Promise promise) throws Exception { List addresses = cache.getIfPresent(inetHost); if (addresses != null) { - promise.trySuccess(addresses.get(0)); + promise.trySuccess(addresses.getFirst()); return; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java index d1df777d..5b69e49a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java @@ -83,7 +83,7 @@ public class JavaPluginLoader implements PluginLoader { @Override public PluginDescription createPluginFromCandidate(PluginDescription candidate) throws Exception { - if (!(candidate instanceof JavaVelocityPluginDescriptionCandidate)) { + if (!(candidate instanceof JavaVelocityPluginDescriptionCandidate candidateInst)) { throw new IllegalArgumentException("Description provided isn't of the Java plugin loader"); } @@ -93,8 +93,6 @@ public class JavaPluginLoader implements PluginLoader { PluginClassLoader loader = new PluginClassLoader(new URL[]{pluginJarUrl}); loader.addToClassloaders(); - JavaVelocityPluginDescriptionCandidate candidateInst = - (JavaVelocityPluginDescriptionCandidate) candidate; Class mainClass = loader.loadClass(candidateInst.getMainClass()); return createDescription(candidateInst, mainClass); } @@ -102,11 +100,10 @@ public class JavaPluginLoader implements PluginLoader { @Override public Module createModule(PluginContainer container) { PluginDescription description = container.getDescription(); - if (!(description instanceof JavaVelocityPluginDescription)) { + if (!(description instanceof JavaVelocityPluginDescription javaDescription)) { throw new IllegalArgumentException("Description provided isn't of the Java plugin loader"); } - JavaVelocityPluginDescription javaDescription = (JavaVelocityPluginDescription) description; Optional source = javaDescription.getSource(); if (source.isEmpty()) { @@ -118,24 +115,23 @@ public class JavaPluginLoader implements PluginLoader { @Override public void createPlugin(PluginContainer container, Module... modules) { - if (!(container instanceof VelocityPluginContainer)) { + if (!(container instanceof VelocityPluginContainer pluginContainer)) { throw new IllegalArgumentException("Container provided isn't of the Java plugin loader"); } - PluginDescription description = container.getDescription(); - if (!(description instanceof JavaVelocityPluginDescription)) { + PluginDescription description = pluginContainer.getDescription(); + if (!(description instanceof JavaVelocityPluginDescription javaPluginDescription)) { throw new IllegalArgumentException("Description provided isn't of the Java plugin loader"); } Injector injector = Guice.createInjector(modules); - Object instance = injector - .getInstance(((JavaVelocityPluginDescription) description).getMainClass()); + Object instance = injector.getInstance(javaPluginDescription.getMainClass()); if (instance == null) { throw new IllegalStateException( "Got nothing from injector for plugin " + description.getId()); } - ((VelocityPluginContainer) container).setInstance(instance); + pluginContainer.setInstance(instance); } private Optional getSerializedPluginInfo(Path source) @@ -145,22 +141,23 @@ public class JavaPluginLoader implements PluginLoader { new BufferedInputStream(Files.newInputStream(source)))) { JarEntry entry; while ((entry = in.getNextJarEntry()) != null) { - if (entry.getName().equals("velocity-plugin.json")) { - try (Reader pluginInfoReader = new InputStreamReader(in, StandardCharsets.UTF_8)) { - return Optional.of(VelocityServer.GENERAL_GSON.fromJson(pluginInfoReader, - SerializedPluginDescription.class)); + switch (entry.getName()) { + case "velocity-plugin.json" -> { + try (Reader pluginInfoReader = new InputStreamReader(in, StandardCharsets.UTF_8)) { + return Optional.of(VelocityServer.GENERAL_GSON.fromJson(pluginInfoReader, + SerializedPluginDescription.class)); + } + } + case "paper-plugin.yml", "plugin.yml", "bungee.yml" -> foundBungeeBukkitPluginFile = true; + default -> { } - } - - if (entry.getName().equals("plugin.yml") || entry.getName().equals("bungee.yml")) { - foundBungeeBukkitPluginFile = true; } } if (foundBungeeBukkitPluginFile) { throw new InvalidPluginException("The plugin file " + source.getFileName() + " appears to " - + "be a Bukkit or BungeeCord plugin. Velocity does not support Bukkit or BungeeCord " - + "plugins."); + + "be a Paper, Bukkit or BungeeCord plugin. Velocity does not support plugins from these " + + "platforms."); } return Optional.empty(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index efc0ed17..826df5b3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -36,7 +36,9 @@ import io.netty.handler.codec.EncoderException; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import net.kyori.adventure.key.Key; import net.kyori.adventure.nbt.BinaryTag; @@ -159,6 +161,9 @@ public enum ProtocolUtils { VAR_INT_LENGTHS[32] = 1; // Special case for the number 0. } + public static final int DEFAULT_MAX_STRING_BYTES = varIntBytes(ByteBufUtil.utf8MaxBytes(DEFAULT_MAX_STRING_SIZE)) + + ByteBufUtil.utf8MaxBytes(DEFAULT_MAX_STRING_SIZE); + private static DecoderException badVarint() { return MinecraftDecoder.DEBUG ? new CorruptedFrameException("Bad VarInt decoded") : BAD_VARINT_CACHED; @@ -415,7 +420,10 @@ public enum ProtocolUtils { */ public static int[] readIntegerArray(ByteBuf buf) { int len = readVarInt(buf); - checkArgument(len >= 0, "Got a negative-length integer array (%s)", len); + checkFrame(len >= 0, "Got a negative-length integer array (%s)", len); + checkFrame(buf.isReadable(len), + "Trying to read an array that is too long (wanted %s, only have %s)", len, + buf.readableBytes()); int[] array = new int[len]; for (int i = 0; i < len; i++) { array[i] = readVarInt(buf); @@ -535,6 +543,10 @@ public enum ProtocolUtils { */ public static String[] readStringArray(ByteBuf buf) { int length = readVarInt(buf); + checkFrame(length >= 0, "Got a negative-length array (%s)", length); + checkFrame(buf.isReadable(length), + "Trying to read an array that is too long (wanted %s, only have %s)", length, + buf.readableBytes()); String[] ret = new String[length]; for (int i = 0; i < length; i++) { ret[i] = readString(buf); @@ -646,6 +658,9 @@ public enum ProtocolUtils { checkArgument(len <= FORGE_MAX_ARRAY_LENGTH, "Cannot receive array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, len); + checkFrame(buf.isReadable(len), + "Trying to read an array that is too long (wanted %s, only have %s)", len, + buf.readableBytes()); byte[] ret = new byte[len]; buf.readBytes(ret); @@ -844,6 +859,29 @@ public enum ProtocolUtils { writeVarInt(buf, source.ordinal()); } + /** + * Returns a pre-sized list with a max initial size of {@code Short.MAX_VALUE}. + * + * @param initialCapacity expected initial capacity + * @param entry type + * @return pre-sized list + */ + public static List newList(int initialCapacity) { + return new ArrayList<>(Math.min(initialCapacity, Short.MAX_VALUE)); + } + + /** + * Returns a pre-sized map with a max initial size of {@code Short.MAX_VALUE}. + * + * @param initialCapacity expected initial capacity + * @param key type + * @param value type + * @return pre-sized map + */ + public static Map newMap(int initialCapacity) { + return new HashMap<>(Math.min(initialCapacity, Short.MAX_VALUE)); + } + /** * Represents the direction in which a packet flows. */ @@ -851,4 +889,4 @@ public enum ProtocolUtils { SERVERBOUND, CLIENTBOUND } -} \ No newline at end of file +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index c778e853..95136cd6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -46,6 +46,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9_4; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_26_1; import static com.velocitypowered.api.network.ProtocolVersion.MINIMUM_VERSION; import static com.velocitypowered.api.network.ProtocolVersion.SUPPORTED_VERSIONS; import static com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; @@ -279,7 +280,8 @@ public enum StateRegistry { map(0x0A, MINECRAFT_1_20_2, false), map(0x0B, MINECRAFT_1_20_5, false), map(0x0D, MINECRAFT_1_21_2, false), - map(0x0E, MINECRAFT_1_21_6, false)); + map(0x0E, MINECRAFT_1_21_6, false), + map(0x0F, MINECRAFT_26_1, false)); serverbound.register( LegacyChatPacket.class, LegacyChatPacket::new, @@ -293,7 +295,8 @@ public enum StateRegistry { ChatAcknowledgementPacket::new, map(0x03, MINECRAFT_1_19_3, false), map(0x04, MINECRAFT_1_21_2, false), - map(0x05, MINECRAFT_1_21_6, false)); + map(0x05, MINECRAFT_1_21_6, false), + map(0x06, MINECRAFT_26_1, false)); serverbound.register(KeyedPlayerCommandPacket.class, KeyedPlayerCommandPacket::new, map(0x03, MINECRAFT_1_19, false), map(0x04, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false)); @@ -304,18 +307,21 @@ public enum StateRegistry { map(0x04, MINECRAFT_1_19_3, false), map(0x05, MINECRAFT_1_20_5, false), map(0x06, MINECRAFT_1_21_2, false), - map(0x07, MINECRAFT_1_21_6, false)); + map(0x07, MINECRAFT_1_21_6, false), + map(0x08, MINECRAFT_26_1, false)); serverbound.register(UnsignedPlayerCommandPacket.class, UnsignedPlayerCommandPacket::new, map(0x04, MINECRAFT_1_20_5, false), map(0x05, MINECRAFT_1_21_2, false), - map(0x06, MINECRAFT_1_21_6, false)); + map(0x06, MINECRAFT_1_21_6, false), + map(0x07, MINECRAFT_26_1, false)); serverbound.register( SessionPlayerChatPacket.class, SessionPlayerChatPacket::new, map(0x05, MINECRAFT_1_19_3, false), map(0x06, MINECRAFT_1_20_5, false), map(0x07, MINECRAFT_1_21_2, false), - map(0x08, MINECRAFT_1_21_6, false)); + map(0x08, MINECRAFT_1_21_6, false), + map(0x09, MINECRAFT_26_1, false)); serverbound.register( ClientSettingsPacket.class, ClientSettingsPacket::new, @@ -331,12 +337,14 @@ public enum StateRegistry { map(0x09, MINECRAFT_1_20_2, false), map(0x0A, MINECRAFT_1_20_5, false), map(0x0C, MINECRAFT_1_21_2, false), - map(0x0D, MINECRAFT_1_21_6, false)); + map(0x0D, MINECRAFT_1_21_6, false), + map(0x0E, MINECRAFT_26_1, false)); serverbound.register( ServerboundCookieResponsePacket.class, ServerboundCookieResponsePacket::new, map(0x11, MINECRAFT_1_20_5, false), map(0x13, MINECRAFT_1_21_2, false), - map(0x14, MINECRAFT_1_21_6, false)); + map(0x14, MINECRAFT_1_21_6, false), + map(0x15, MINECRAFT_26_1, false)); serverbound.register( PluginMessagePacket.class, PluginMessagePacket::new, @@ -355,7 +363,8 @@ public enum StateRegistry { map(0x10, MINECRAFT_1_20_3, false), map(0x12, MINECRAFT_1_20_5, false), map(0x14, MINECRAFT_1_21_2, false), - map(0x15, MINECRAFT_1_21_6, false)); + map(0x15, MINECRAFT_1_21_6, false), + map(0x16, MINECRAFT_26_1, false)); serverbound.register( KeepAlivePacket.class, KeepAlivePacket::new, @@ -375,7 +384,8 @@ public enum StateRegistry { map(0x15, MINECRAFT_1_20_3, false), map(0x18, MINECRAFT_1_20_5, false), map(0x1A, MINECRAFT_1_21_2, false), - map(0x1B, MINECRAFT_1_21_6, false)); + map(0x1B, MINECRAFT_1_21_6, false), + map(0x1C, MINECRAFT_26_1, false)); serverbound.register( ResourcePackResponsePacket.class, ResourcePackResponsePacket::new, @@ -393,13 +403,15 @@ public enum StateRegistry { map(0x2B, MINECRAFT_1_20_5, false), map(0x2D, MINECRAFT_1_21_2, false), map(0x2F, MINECRAFT_1_21_4, false), - map(0x30, MINECRAFT_1_21_6, false)); + map(0x30, MINECRAFT_1_21_6, false), + map(0x31, MINECRAFT_26_1, false)); serverbound.register( FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE, map(0x0B, MINECRAFT_1_20_2, false), map(0x0C, MINECRAFT_1_20_5, false), map(0x0E, MINECRAFT_1_21_2, false), - map(0x0F, MINECRAFT_1_21_6, false)); + map(0x0F, MINECRAFT_1_21_6, false), + map(0x10, MINECRAFT_26_1, false)); clientbound.register( BossBarPacket.class, @@ -460,7 +472,8 @@ public enum StateRegistry { map(0x67, MINECRAFT_1_20_5, true), map(0x6E, MINECRAFT_1_21_2, true), map(0x6D, MINECRAFT_1_21_5, true), - map(0x72, MINECRAFT_1_21_9, true)); + map(0x72, MINECRAFT_1_21_9, true), + map(0x74, MINECRAFT_26_1, true)); clientbound.register( ClientboundStopSoundPacket.class, ClientboundStopSoundPacket::new, map(0x5F, MINECRAFT_1_19_3, true), @@ -470,7 +483,8 @@ public enum StateRegistry { map(0x6A, MINECRAFT_1_20_5, true), map(0x71, MINECRAFT_1_21_2, true), map(0x70, MINECRAFT_1_21_5, true), - map(0x75, MINECRAFT_1_21_9, true)); + map(0x75, MINECRAFT_1_21_9, true), + map(0x77, MINECRAFT_26_1, true)); clientbound.register( PluginMessagePacket.class, PluginMessagePacket::new, @@ -527,7 +541,8 @@ public enum StateRegistry { map(0x26, MINECRAFT_1_20_5, false), map(0x27, MINECRAFT_1_21_2, false), map(0x26, MINECRAFT_1_21_5, false), - map(0x2B, MINECRAFT_1_21_9, false)); + map(0x2B, MINECRAFT_1_21_9, false), + map(0x2C, MINECRAFT_26_1, false)); clientbound.register( JoinGamePacket.class, JoinGamePacket::new, @@ -547,7 +562,8 @@ public enum StateRegistry { map(0x2B, MINECRAFT_1_20_5, false), map(0x2C, MINECRAFT_1_21_2, false), map(0x2B, MINECRAFT_1_21_5, false), - map(0x30, MINECRAFT_1_21_9, false)); + map(0x30, MINECRAFT_1_21_9, false), + map(0x31, MINECRAFT_26_1, false)); clientbound.register( RespawnPacket.class, RespawnPacket::new, @@ -570,7 +586,8 @@ public enum StateRegistry { map(0x47, MINECRAFT_1_20_5, true), map(0x4C, MINECRAFT_1_21_2, true), map(0x4B, MINECRAFT_1_21_5, true), - map(0x50, MINECRAFT_1_21_9, true)); + map(0x50, MINECRAFT_1_21_9, true), + map(0x52, MINECRAFT_26_1, true)); clientbound.register( RemoveResourcePackPacket.class, RemoveResourcePackPacket::new, @@ -578,7 +595,8 @@ public enum StateRegistry { map(0x45, MINECRAFT_1_20_5, false), map(0x4A, MINECRAFT_1_21_2, false), map(0x49, MINECRAFT_1_21_5, false), - map(0x4E, MINECRAFT_1_21_9, false)); + map(0x4E, MINECRAFT_1_21_9, false), + map(0x50, MINECRAFT_26_1, false)); clientbound.register( ResourcePackRequestPacket.class, ResourcePackRequestPacket::new, @@ -601,7 +619,8 @@ public enum StateRegistry { map(0x46, MINECRAFT_1_20_5, false), map(0x4B, MINECRAFT_1_21_2, false), map(0x4A, MINECRAFT_1_21_5, false), - map(0x4F, MINECRAFT_1_21_9, false)); + map(0x4F, MINECRAFT_1_21_9, false), + map(0x51, MINECRAFT_26_1, false)); clientbound.register( HeaderAndFooterPacket.class, HeaderAndFooterPacket::new, @@ -625,7 +644,8 @@ public enum StateRegistry { map(0x6D, MINECRAFT_1_20_5, true), map(0x74, MINECRAFT_1_21_2, true), map(0x73, MINECRAFT_1_21_5, true), - map(0x78, MINECRAFT_1_21_9, true)); + map(0x78, MINECRAFT_1_21_9, true), + map(0x7A, MINECRAFT_26_1, true)); clientbound.register( LegacyTitlePacket.class, LegacyTitlePacket::new, @@ -648,7 +668,8 @@ public enum StateRegistry { map(0x63, MINECRAFT_1_20_5, true), map(0x6A, MINECRAFT_1_21_2, true), map(0x69, MINECRAFT_1_21_5, true), - map(0x6E, MINECRAFT_1_21_9, true)); + map(0x6E, MINECRAFT_1_21_9, true), + map(0x70, MINECRAFT_26_1, true)); clientbound.register( TitleTextPacket.class, TitleTextPacket::new, @@ -662,7 +683,8 @@ public enum StateRegistry { map(0x65, MINECRAFT_1_20_5, true), map(0x6C, MINECRAFT_1_21_2, true), map(0x6B, MINECRAFT_1_21_5, true), - map(0x70, MINECRAFT_1_21_9, true)); + map(0x70, MINECRAFT_1_21_9, true), + map(0x72, MINECRAFT_26_1, true)); clientbound.register( TitleActionbarPacket.class, TitleActionbarPacket::new, @@ -676,7 +698,8 @@ public enum StateRegistry { map(0x4C, MINECRAFT_1_20_5, true), map(0x51, MINECRAFT_1_21_2, true), map(0x50, MINECRAFT_1_21_5, true), - map(0x55, MINECRAFT_1_21_9, true)); + map(0x55, MINECRAFT_1_21_9, true), + map(0x57, MINECRAFT_26_1, true)); clientbound.register( TitleTimesPacket.class, TitleTimesPacket::new, @@ -690,7 +713,8 @@ public enum StateRegistry { map(0x66, MINECRAFT_1_20_5, true), map(0x6D, MINECRAFT_1_21_2, true), map(0x6C, MINECRAFT_1_21_5, true), - map(0x71, MINECRAFT_1_21_9, true)); + map(0x71, MINECRAFT_1_21_9, true), + map(0x73, MINECRAFT_26_1, true)); clientbound.register( TitleClearPacket.class, TitleClearPacket::new, @@ -721,7 +745,8 @@ public enum StateRegistry { map(0x3D, MINECRAFT_1_20_5, false), map(0x3F, MINECRAFT_1_21_2, false), map(0x3E, MINECRAFT_1_21_5, false), - map(0x43, MINECRAFT_1_21_9, false)); + map(0x43, MINECRAFT_1_21_9, false), + map(0x45, MINECRAFT_26_1, false)); clientbound.register( UpsertPlayerInfoPacket.class, UpsertPlayerInfoPacket::new, @@ -731,13 +756,15 @@ public enum StateRegistry { map(0x3E, MINECRAFT_1_20_5, false), map(0x40, MINECRAFT_1_21_2, false), map(0x3F, MINECRAFT_1_21_5, false), - map(0x44, MINECRAFT_1_21_9, false)); + map(0x44, MINECRAFT_1_21_9, false), + map(0x46, MINECRAFT_26_1, false)); clientbound.register( ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new, map(0x6B, MINECRAFT_1_20_5, false), map(0x72, MINECRAFT_1_21_2, false), map(0x71, MINECRAFT_1_21_5, false), - map(0x76, MINECRAFT_1_21_9, false)); + map(0x76, MINECRAFT_1_21_9, false), + map(0x78, MINECRAFT_26_1, false)); clientbound.register( SystemChatPacket.class, SystemChatPacket::new, @@ -750,7 +777,8 @@ public enum StateRegistry { map(0x6C, MINECRAFT_1_20_5, true), map(0x73, MINECRAFT_1_21_2, true), map(0x72, MINECRAFT_1_21_5, true), - map(0x77, MINECRAFT_1_21_9, true)); + map(0x77, MINECRAFT_1_21_9, true), + map(0x79, MINECRAFT_26_1, true)); clientbound.register( PlayerChatCompletionPacket.class, PlayerChatCompletionPacket::new, @@ -772,7 +800,8 @@ public enum StateRegistry { map(0x4B, MINECRAFT_1_20_5, false), map(0x50, MINECRAFT_1_21_2, false), map(0x4F, MINECRAFT_1_21_5, false), - map(0x54, MINECRAFT_1_21_9, false)); + map(0x54, MINECRAFT_1_21_9, false), + map(0x56, MINECRAFT_26_1, false)); clientbound.register( StartUpdatePacket.class, () -> StartUpdatePacket.INSTANCE, @@ -781,7 +810,8 @@ public enum StateRegistry { map(0x69, MINECRAFT_1_20_5, false), map(0x70, MINECRAFT_1_21_2, false), map(0x6F, MINECRAFT_1_21_5, false), - map(0x74, MINECRAFT_1_21_9, false)); + map(0x74, MINECRAFT_1_21_9, false), + map(0x76, MINECRAFT_26_1, false)); clientbound.register( BundleDelimiterPacket.class, () -> BundleDelimiterPacket.INSTANCE, @@ -791,13 +821,15 @@ public enum StateRegistry { TransferPacket::new, map(0x73, MINECRAFT_1_20_5, false), map(0x7A, MINECRAFT_1_21_2, false), - map(0x7F, MINECRAFT_1_21_9, false)); + map(0x7F, MINECRAFT_1_21_9, false), + map(0x81, MINECRAFT_26_1, false)); clientbound.register( ClientboundCustomReportDetailsPacket.class, ClientboundCustomReportDetailsPacket::new, map(0x7A, MINECRAFT_1_21, false), map(0x81, MINECRAFT_1_21_2, false), - map(0x86, MINECRAFT_1_21_9, false)); + map(0x86, MINECRAFT_1_21_9, false), + map(0x88, MINECRAFT_26_1, false)); clientbound.register( ClientboundServerLinksPacket.class, ClientboundServerLinksPacket::new, @@ -819,7 +851,8 @@ public enum StateRegistry { map(0x5E, ProtocolVersion.MINECRAFT_1_20_3, true), map(0x60, ProtocolVersion.MINECRAFT_1_20_5, true), map(0x67, ProtocolVersion.MINECRAFT_1_21_2, true) - ); + map(0x87, MINECRAFT_1_21_9, false), + map(0x89, MINECRAFT_26_1, false)); } }, LOGIN { @@ -848,7 +881,7 @@ public enum StateRegistry { map(0x01, MINECRAFT_1_7_2, false)); clientbound.register( ServerLoginSuccessPacket.class, ServerLoginSuccessPacket::new, - map(0x02, MINECRAFT_1_7_2, false)); + map(0x02, MINECRAFT_1_7_2, false)); clientbound.register( SetCompressionPacket.class, SetCompressionPacket::new, map(0x03, MINECRAFT_1_8, false)); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GameSpyQueryHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GameSpyQueryHandler.java index 57c0b318..b22a2442 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GameSpyQueryHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GameSpyQueryHandler.java @@ -119,7 +119,7 @@ public class GameSpyQueryHandler extends SimpleChannelInboundHandler { // Generate new challenge token and put it into the sessions cache int challengeToken = random.nextInt(); sessions.put(senderAddress, challengeToken); @@ -132,10 +132,9 @@ public class GameSpyQueryHandler extends SimpleChannelInboundHandler { // Check if query was done with session previously generated using a handshake packet int challengeToken = queryMessage.readInt(); Integer session = sessions.getIfPresent(senderAddress); @@ -190,10 +189,10 @@ public class GameSpyQueryHandler extends SimpleChannelInboundHandler { + // Invalid query type - just don't respond + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java index 1fe38e50..90b747b0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java @@ -33,20 +33,34 @@ import java.util.List; */ public class MinecraftCompressDecoder extends MessageToMessageDecoder { + private static final int SERVERBOUND_MAXIMUM_UNCOMPRESSED_SIZE = 2 * 1024 * 1024; // 2MiB private static final int VANILLA_MAXIMUM_UNCOMPRESSED_SIZE = 8 * 1024 * 1024; // 8MiB private static final int HARD_MAXIMUM_UNCOMPRESSED_SIZE = 128 * 1024 * 1024; // 128MiB - private static final int UNCOMPRESSED_CAP = + private static final int CLIENTBOUND_UNCOMPRESSED_CAP = Boolean.getBoolean("velocity.increased-compression-cap") ? HARD_MAXIMUM_UNCOMPRESSED_SIZE : VANILLA_MAXIMUM_UNCOMPRESSED_SIZE; + private static final int SERVERBOUND_UNCOMPRESSED_CAP = + Boolean.getBoolean("velocity.increased-compression-cap") + ? HARD_MAXIMUM_UNCOMPRESSED_SIZE : SERVERBOUND_MAXIMUM_UNCOMPRESSED_SIZE; private static final boolean SKIP_COMPRESSION_VALIDATION = Boolean.getBoolean("velocity.skip-uncompressed-packet-size-validation"); + private static final double MAX_COMPRESSION_RATIO = Double.parseDouble(System.getProperty("velocity.max-compression-ratio", "64")); + private final ProtocolUtils.Direction direction; private int threshold; private final VelocityCompressor compressor; - public MinecraftCompressDecoder(int threshold, VelocityCompressor compressor) { + /** + * Creates a new {@code MinecraftCompressDecoder} with the specified compression {@code threshold}. + * + * @param threshold the threshold for compression. Packets with uncompressed size below this threshold will not be compressed. + * @param compressor the compressor instance to use + * @param direction the direction of the packets being decoded + */ + public MinecraftCompressDecoder(int threshold, VelocityCompressor compressor, ProtocolUtils.Direction direction) { this.threshold = threshold; this.compressor = compressor; + this.direction = direction; } @Override @@ -62,17 +76,29 @@ public class MinecraftCompressDecoder extends MessageToMessageDecoder { out.add(in.retain()); return; } + int length = in.readableBytes(); checkFrame(claimedUncompressedSize >= threshold, "Uncompressed size %s is less than" + " threshold %s", claimedUncompressedSize, threshold); - checkFrame(claimedUncompressedSize <= UNCOMPRESSED_CAP, - "Uncompressed size %s exceeds hard threshold of %s", claimedUncompressedSize, - UNCOMPRESSED_CAP); - + if (direction == ProtocolUtils.Direction.CLIENTBOUND) { + checkFrame(claimedUncompressedSize <= CLIENTBOUND_UNCOMPRESSED_CAP, + "Uncompressed size %s exceeds hard threshold of %s", claimedUncompressedSize, + CLIENTBOUND_UNCOMPRESSED_CAP); + } else { + checkFrame(claimedUncompressedSize <= SERVERBOUND_UNCOMPRESSED_CAP, + "Uncompressed size %s exceeds hard threshold of %s", claimedUncompressedSize, + SERVERBOUND_UNCOMPRESSED_CAP); + double maxCompressedAllowed = length * MAX_COMPRESSION_RATIO; + checkFrame(claimedUncompressedSize <= maxCompressedAllowed, + "Uncompressed size %s exceeds ratio threshold of %s for compressed sized %s", claimedUncompressedSize, + maxCompressedAllowed, length); + } ByteBuf compatibleIn = ensureCompatible(ctx.alloc(), compressor, in); ByteBuf uncompressed = preferredBuffer(ctx.alloc(), compressor, claimedUncompressedSize); try { compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize); + checkFrame(uncompressed.writerIndex() == claimedUncompressedSize, + "Decompressed size %s does not match claimed uncompressed size %s", uncompressed.writerIndex(), claimedUncompressedSize); out.add(uncompressed); } catch (Exception e) { uncompressed.release(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java index 35ddccd5..ce0d3426 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java @@ -57,7 +57,11 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof ByteBuf buf) { - tryDecode(ctx, buf); + try { + tryDecode(ctx, buf); + } finally { + buf.release(); + } } else { ctx.fireChannelRead(msg); } @@ -65,7 +69,6 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter { private void tryDecode(ChannelHandlerContext ctx, ByteBuf buf) throws Exception { if (!ctx.channel().isActive() || !buf.isReadable()) { - buf.release(); return; } @@ -74,24 +77,23 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter { MinecraftPacket packet = this.registry.createPacket(packetId); if (packet == null) { buf.readerIndex(originalReaderIndex); - ctx.fireChannelRead(buf); - } else { - try { - doLengthSanityChecks(buf, packet); - - try { - packet.decode(buf, direction, registry.version); - } catch (Exception e) { - throw handleDecodeFailure(e, packet, packetId); - } - - if (buf.isReadable()) { - throw handleOverflow(packet, buf.readerIndex(), buf.writerIndex()); - } - ctx.fireChannelRead(packet); - } finally { - buf.release(); + if (this.direction == ProtocolUtils.Direction.SERVERBOUND && this.state != StateRegistry.PLAY) { + throw this.handleInvalidPacketId(packetId); } + ctx.fireChannelRead(buf.retain()); + } else { + doLengthSanityChecks(buf, packet); + + try { + packet.decode(buf, direction, registry.version); + } catch (Exception e) { + throw handleDecodeFailure(e, packet, packetId); + } + + if (buf.isReadable()) { + throw handleOverflow(packet, buf.readerIndex(), buf.writerIndex()); + } + ctx.fireChannelRead(packet); } } @@ -133,6 +135,14 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter { } } + private Exception handleInvalidPacketId(int packetId) { + if (DEBUG) { + return new CorruptedFrameException("Invalid packet " + getExtraConnectionDetail(packetId)); + } else { + return DECODE_FAILED; + } + } + private String getExtraConnectionDetail(int packetId) { return "Direction " + direction + " Protocol " + registry.version + " State " + state + " ID 0x" + Integer.toHexString(packetId); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java index 7d4d8d6d..56c58619 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java @@ -20,6 +20,7 @@ package com.velocitypowered.proxy.protocol.netty; import static io.netty.util.ByteProcessor.FIND_NON_NUL; import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.network.limiter.PacketLimiter; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; @@ -32,6 +33,7 @@ import io.netty.handler.codec.CorruptedFrameException; import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jspecify.annotations.Nullable; /** * Frames Minecraft server packets which are prefixed by a 21-bit VarInt encoding. @@ -44,6 +46,8 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { + "Velocity with -Dvelocity.packet-decode-logging=true to see more."); private static final QuietDecoderException BAD_PACKET_LENGTH = new QuietDecoderException("Bad packet length"); + private static final QuietDecoderException INVALID_PREAMBLE = + new QuietDecoderException("Invalid packet preamble"); private static final QuietDecoderException VARINT_TOO_BIG = new QuietDecoderException("VarInt too big"); private static final QuietDecoderException UNKNOWN_PACKET = @@ -52,6 +56,8 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { private final ProtocolUtils.Direction direction; private final StateRegistry.PacketRegistry.ProtocolRegistry registry; private StateRegistry state; + @Nullable + private PacketLimiter packetLimiter; /** * Creates a new {@code MinecraftVarintFrameDecoder} decoding packets from the specified {@code Direction}. @@ -74,38 +80,58 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { } // skip any runs of 0x00 we might find + int wlen = in.readableBytes(); int packetStart = in.forEachByte(FIND_NON_NUL); if (packetStart == -1) { in.clear(); + // Apply a more strict check in serverbound direction, we really shouldn't be seeing this many 0x00s + // even from the server, the only reason we even allow these is due to bugged servers + if (direction == ProtocolUtils.Direction.SERVERBOUND && wlen > 16) { + throw INVALID_PREAMBLE; + } return; } in.readerIndex(packetStart); // try to read the length of the packet - in.markReaderIndex(); - int length = readRawVarInt21(in); - if (packetStart == in.readerIndex()) { - return; - } - if (length < 0) { - throw BAD_PACKET_LENGTH; - } + try { + int length = readRawVarInt21(in); + if (packetStart == in.readerIndex()) { + return; + } + if (length < 0) { + throw BAD_PACKET_LENGTH; + } - if (length > 0) { - if (state == StateRegistry.HANDSHAKE && direction == ProtocolUtils.Direction.SERVERBOUND) { - if (validateServerboundHandshakePacket(in, length)) { - return; + if (length > 0) { + if (state == StateRegistry.HANDSHAKE && direction == ProtocolUtils.Direction.SERVERBOUND) { + if (validateServerboundHandshakePacket(in, length)) { + in.readerIndex(packetStart); + return; + } } } - } - // note that zero-length packets are ignored - if (length > 0) { - if (in.readableBytes() < length) { - in.resetReaderIndex(); - } else { - out.add(in.readRetainedSlice(length)); + // note that zero-length packets are ignored + if (length > 0) { + if (in.readableBytes() < length) { + in.readerIndex(packetStart); + } else { + // If enabled, rate-limit serverbound payload bytes based on frame length + if (packetLimiter != null) { + if (!packetLimiter.account(length)) { + throw new QuietDecoderException( + "Rate limit exceeded while processing packets for %s".formatted( + ctx.channel().remoteAddress())); + } + } + out.add(in.readRetainedSlice(length)); + } } + } catch (Exception e) { + // Reset buffer to consistent state before propagating exception to prevent memory leaks + in.readerIndex(packetStart); + throw e; } } @@ -117,7 +143,6 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { final int packetId = readRawVarInt21(in); // Index hasn't changed, we've read nothing if (index == in.readerIndex()) { - in.resetReaderIndex(); return true; } final int payloadLength = length - ProtocolUtils.varIntBytes(packetId); @@ -245,4 +270,8 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { public void setState(StateRegistry stateRegistry) { this.state = stateRegistry; } + + public void setPacketLimiter(@Nullable PacketLimiter packetLimiter) { + this.packetLimiter = packetLimiter; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueInboundHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueInboundHandler.java index 1affc13b..899768d2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueInboundHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueInboundHandler.java @@ -21,6 +21,9 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.util.except.QuietDecoderException; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufHolder; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.util.ReferenceCountUtil; @@ -41,8 +44,13 @@ import org.jetbrains.annotations.NotNull; */ public class PlayPacketQueueInboundHandler extends ChannelDuplexHandler { + private static final int MAXIMUM_SIZE = Integer.getInteger("velocity.maximum-play-queue-size", 128 * 1024 * 1024); // 128MiB by default + private static final QuietDecoderException QUEUE_LIMIT_FAILED = new QuietDecoderException( + "Queue too big (greater than " + MAXIMUM_SIZE + " bytes)"); + private final StateRegistry.PacketRegistry.ProtocolRegistry registry; private final Queue queue = new ArrayDeque<>(); + private int queueSize = 0; /** * Provides registries for client & server bound packets. @@ -64,6 +72,20 @@ public class PlayPacketQueueInboundHandler extends ChannelDuplexHandler { } } + int length = 0; + if (msg instanceof ByteBuf) { + // keep track of raw packets + length = ((ByteBuf) msg).readableBytes(); + } else if (msg instanceof ByteBufHolder) { + // keep track of bytebufs wrapped inside packets + length = ((ByteBufHolder) msg).content().readableBytes(); + } + if (this.queueSize + length > MAXIMUM_SIZE) { + ReferenceCountUtil.release(msg); + throw QUEUE_LIMIT_FAILED; + } + this.queueSize += length; + // Otherwise, queue the packet this.queue.offer(msg); } @@ -90,5 +112,6 @@ public class PlayPacketQueueInboundHandler extends ChannelDuplexHandler { ReferenceCountUtil.release(msg); } } + this.queueSize = 0; } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java index d3fc3a82..6653d763 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java @@ -25,7 +25,6 @@ import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; @@ -45,9 +44,9 @@ import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenCustomHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import java.util.ArrayDeque; -import java.util.Arrays; import java.util.Deque; import java.util.Iterator; +import java.util.List; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; @@ -86,14 +85,14 @@ public class AvailableCommandsPacket implements MinecraftPacket { @Override public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { int commands = ProtocolUtils.readVarInt(buf); - WireNode[] wireNodes = new WireNode[commands]; + List wireNodes = ProtocolUtils.newList(commands); for (int i = 0; i < commands; i++) { - wireNodes[i] = deserializeNode(buf, i, protocolVersion); + wireNodes.add(deserializeNode(buf, i, protocolVersion)); } // Iterate over the deserialized nodes and attempt to form a graph. We also resolve any cycles // that exist. - Queue nodeQueue = new ArrayDeque<>(Arrays.asList(wireNodes)); + Queue nodeQueue = new ArrayDeque<>(wireNodes); while (!nodeQueue.isEmpty()) { boolean cycling = false; @@ -112,7 +111,7 @@ public class AvailableCommandsPacket implements MinecraftPacket { } int rootIdx = ProtocolUtils.readVarInt(buf); - rootNode = (RootCommandNode) wireNodes[rootIdx].built; + rootNode = (RootCommandNode) wireNodes.get(rootIdx).built; } @Override @@ -246,17 +245,17 @@ public class AvailableCommandsPacket implements MinecraftPacket { this.validated = false; } - void validate(WireNode[] wireNodes) { + void validate(List wireNodes) { // Ensure all children exist. Note that we delay checking if the node has been built yet; // that needs to come after this node is built. for (int child : children) { - if (child < 0 || child >= wireNodes.length) { + if (child < 0 || child >= wireNodes.size()) { throw new IllegalStateException("Node points to non-existent index " + child); } } if (redirectTo != -1) { - if (redirectTo < 0 || redirectTo >= wireNodes.length) { + if (redirectTo < 0 || redirectTo >= wireNodes.size()) { throw new IllegalStateException("Redirect node points to non-existent index " + redirectTo); } @@ -265,7 +264,7 @@ public class AvailableCommandsPacket implements MinecraftPacket { this.validated = true; } - boolean toNode(WireNode[] wireNodes) { + boolean toNode(List wireNodes) { if (!this.validated) { this.validate(wireNodes); } @@ -281,7 +280,7 @@ public class AvailableCommandsPacket implements MinecraftPacket { // Add any redirects if (redirectTo != -1) { - WireNode redirect = wireNodes[redirectTo]; + WireNode redirect = wireNodes.get(redirectTo); if (redirect.built != null) { args.redirect(redirect.built); } else { @@ -305,7 +304,7 @@ public class AvailableCommandsPacket implements MinecraftPacket { } for (int child : children) { - if (wireNodes[child].built == null) { + if (wireNodes.get(child).built == null) { // The child is not yet deserialized. The node can't be built now. return false; } @@ -313,7 +312,7 @@ public class AvailableCommandsPacket implements MinecraftPacket { // Associate children with nodes for (int child : children) { - CommandNode childNode = wireNodes[child].built; + CommandNode childNode = wireNodes.get(child).built; if (!(childNode instanceof RootCommandNode)) { built.addChild(childNode); } @@ -331,12 +330,10 @@ public class AvailableCommandsPacket implements MinecraftPacket { .add("redirectTo", redirectTo); if (args != null) { - if (args instanceof LiteralArgumentBuilder) { - helper.add("argsLabel", - ((LiteralArgumentBuilder) args).getLiteral()); - } else if (args instanceof RequiredArgumentBuilder) { - helper.add("argsName", - ((RequiredArgumentBuilder) args).getName()); + if (args instanceof LiteralArgumentBuilder literal) { + helper.add("argsLabel", literal.getLiteral()); + } else if (args instanceof RequiredArgumentBuilder required) { + helper.add("argsName", required.getName()); } } @@ -348,17 +345,11 @@ public class AvailableCommandsPacket implements MinecraftPacket { * A placeholder {@link SuggestionProvider} used internally to preserve the suggestion provider * name. */ - public static class ProtocolSuggestionProvider implements SuggestionProvider { - - private final String name; - - public ProtocolSuggestionProvider(String name) { - this.name = name; - } + public record ProtocolSuggestionProvider(String name) implements SuggestionProvider { @Override public CompletableFuture getSuggestions(CommandContext context, - SuggestionsBuilder builder) throws CommandSyntaxException { + SuggestionsBuilder builder) { return builder.buildFuture(); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/BossBarPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/BossBarPacket.java index cc4c2671..d7748eff 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/BossBarPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/BossBarPacket.java @@ -207,30 +207,22 @@ public class BossBarPacket implements MinecraftPacket { this.uuid = ProtocolUtils.readUuid(buf); this.action = ProtocolUtils.readVarInt(buf); switch (action) { - case ADD: + case ADD -> { this.name = ComponentHolder.read(buf, version); this.percent = buf.readFloat(); this.color = ProtocolUtils.readVarInt(buf); this.overlay = ProtocolUtils.readVarInt(buf); this.flags = buf.readUnsignedByte(); - break; - case REMOVE: - break; - case UPDATE_PERCENT: - this.percent = buf.readFloat(); - break; - case UPDATE_NAME: - this.name = ComponentHolder.read(buf, version); - break; - case UPDATE_STYLE: + } + case REMOVE -> {} + case UPDATE_PERCENT -> this.percent = buf.readFloat(); + case UPDATE_NAME -> this.name = ComponentHolder.read(buf, version); + case UPDATE_STYLE -> { this.color = ProtocolUtils.readVarInt(buf); this.overlay = ProtocolUtils.readVarInt(buf); - break; - case UPDATE_PROPERTIES: - this.flags = buf.readUnsignedByte(); - break; - default: - throw new UnsupportedOperationException("Unknown action " + action); + } + case UPDATE_PROPERTIES -> this.flags = buf.readUnsignedByte(); + default -> throw new UnsupportedOperationException("Unknown action " + action); } } @@ -242,36 +234,30 @@ public class BossBarPacket implements MinecraftPacket { ProtocolUtils.writeUuid(buf, uuid); ProtocolUtils.writeVarInt(buf, action); switch (action) { - case ADD: - if (name == null) { - throw new IllegalStateException("No name specified!"); - } - name.write(buf); - buf.writeFloat(percent); + case ADD -> { + if (name == null) { + throw new IllegalStateException("No name specified!"); + } + name.write(buf); + buf.writeFloat(percent); + ProtocolUtils.writeVarInt(buf, color); + ProtocolUtils.writeVarInt(buf, overlay); + buf.writeByte(flags); + } + case REMOVE -> {} + case UPDATE_PERCENT -> buf.writeFloat(percent); + case UPDATE_NAME -> { + if (name == null) { + throw new IllegalStateException("No name specified!"); + } + name.write(buf); + } + case UPDATE_STYLE -> { ProtocolUtils.writeVarInt(buf, color); ProtocolUtils.writeVarInt(buf, overlay); - buf.writeByte(flags); - break; - case REMOVE: - break; - case UPDATE_PERCENT: - buf.writeFloat(percent); - break; - case UPDATE_NAME: - if (name == null) { - throw new IllegalStateException("No name specified!"); - } - name.write(buf); - break; - case UPDATE_STYLE: - ProtocolUtils.writeVarInt(buf, color); - ProtocolUtils.writeVarInt(buf, overlay); - break; - case UPDATE_PROPERTIES: - buf.writeByte(flags); - break; - default: - throw new UnsupportedOperationException("Unknown action " + action); + } + case UPDATE_PROPERTIES -> buf.writeByte(flags); + default -> throw new UnsupportedOperationException("Unknown action " + action); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java index 39e6fde0..b8b60a9f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java @@ -22,8 +22,8 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import java.util.Objects; - import org.checkerframework.checker.nullness.qual.Nullable; public class ClientSettingsPacket implements MinecraftPacket { @@ -135,7 +135,7 @@ public class ClientSettingsPacket implements MinecraftPacket { return "ClientSettings{" + "locale='" + locale + '\'' + ", viewDistance=" + viewDistance + ", chatVisibility=" + chatVisibility + ", chatColors=" + chatColors + ", skinParts=" + skinParts + ", mainHand=" + mainHand + ", chatFilteringEnabled=" + textFilteringEnabled + - ", clientListingAllowed=" + clientListingAllowed + ", particleStatus=" + particleStatus + '}'; + ", clientListingAllowed=" + clientListingAllowed + ", particleStatus=" + particleStatus + '}'; } @Override @@ -206,6 +206,16 @@ public class ClientSettingsPacket implements MinecraftPacket { return handler.handle(this); } + @Override + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { + return 1 + ByteBufUtil.utf8MaxBytes(16) + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1; + } + + @Override + public int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { + return 1 + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1; + } + @Override public boolean equals(@Nullable final Object o) { if (this == o) { @@ -237,7 +247,7 @@ public class ClientSettingsPacket implements MinecraftPacket { difficulty, skinParts, mainHand, - textFilteringEnabled, + textFilteringEnabled, clientListingAllowed, particleStatus); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlivePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlivePacket.java index a44e50ee..932dd47a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlivePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlivePacket.java @@ -64,6 +64,28 @@ public class KeepAlivePacket implements MinecraftPacket { } } + @Override + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_12_2)) { + return Long.BYTES; + } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + return 5; + } else { + return Integer.BYTES; + } + } + + @Override + public int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_12_2)) { + return Long.BYTES; + } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + return 1; + } else { + return Integer.BYTES; + } + } + @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPlayerListItemPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPlayerListItemPacket.java index 268c707d..9dbe9cbc 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPlayerListItemPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPlayerListItemPacket.java @@ -69,33 +69,25 @@ public class LegacyPlayerListItemPacket implements MinecraftPacket { Item item = new Item(ProtocolUtils.readUuid(buf)); items.add(item); switch (action) { - case ADD_PLAYER: + case ADD_PLAYER -> { item.setName(ProtocolUtils.readString(buf)); item.setProperties(ProtocolUtils.readProperties(buf)); item.setGameMode(ProtocolUtils.readVarInt(buf)); item.setLatency(ProtocolUtils.readVarInt(buf)); item.setDisplayName(readOptionalComponent(buf, version)); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - if (buf.readBoolean()) { - item.setPlayerKey(ProtocolUtils.readPlayerKey(version, buf)); - } + if (buf.readBoolean()) { + item.setPlayerKey(ProtocolUtils.readPlayerKey(version, buf)); + } } - break; - case UPDATE_GAMEMODE: - item.setGameMode(ProtocolUtils.readVarInt(buf)); - break; - case UPDATE_LATENCY: - item.setLatency(ProtocolUtils.readVarInt(buf)); - break; - case UPDATE_DISPLAY_NAME: - item.setDisplayName(readOptionalComponent(buf, version)); - break; - case REMOVE_PLAYER: - //Do nothing, all that is needed is the uuid - break; - default: - throw new UnsupportedOperationException("Unknown action " + action); + } + case UPDATE_GAMEMODE -> item.setGameMode(ProtocolUtils.readVarInt(buf)); + case UPDATE_LATENCY -> item.setLatency(ProtocolUtils.readVarInt(buf)); + case UPDATE_DISPLAY_NAME -> item.setDisplayName(readOptionalComponent(buf, version)); + case REMOVE_PLAYER -> { + //Do nothing, all that is needed is the uuid + } + default -> throw new UnsupportedOperationException("Unknown action " + action); } } } else { @@ -126,39 +118,32 @@ public class LegacyPlayerListItemPacket implements MinecraftPacket { ProtocolUtils.writeUuid(buf, uuid); switch (action) { - case ADD_PLAYER: + case ADD_PLAYER -> { ProtocolUtils.writeString(buf, item.getName()); ProtocolUtils.writeProperties(buf, item.getProperties()); ProtocolUtils.writeVarInt(buf, item.getGameMode()); ProtocolUtils.writeVarInt(buf, item.getLatency()); writeDisplayName(buf, item.getDisplayName(), version); if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - if (item.getPlayerKey() != null) { - buf.writeBoolean(true); - ProtocolUtils.writePlayerKey(buf, item.getPlayerKey()); - } else { - buf.writeBoolean(false); - } + if (item.getPlayerKey() != null) { + buf.writeBoolean(true); + ProtocolUtils.writePlayerKey(buf, item.getPlayerKey()); + } else { + buf.writeBoolean(false); + } } - break; - case UPDATE_GAMEMODE: - ProtocolUtils.writeVarInt(buf, item.getGameMode()); - break; - case UPDATE_LATENCY: - ProtocolUtils.writeVarInt(buf, item.getLatency()); - break; - case UPDATE_DISPLAY_NAME: - writeDisplayName(buf, item.getDisplayName(), version); - break; - case REMOVE_PLAYER: + } + case UPDATE_GAMEMODE -> ProtocolUtils.writeVarInt(buf, item.getGameMode()); + case UPDATE_LATENCY -> ProtocolUtils.writeVarInt(buf, item.getLatency()); + case UPDATE_DISPLAY_NAME -> writeDisplayName(buf, item.getDisplayName(), version); + case REMOVE_PLAYER -> { // Do nothing, all that is needed is the uuid - break; - default: - throw new UnsupportedOperationException("Unknown action " + action); + } + default -> throw new UnsupportedOperationException("Unknown action " + action); } } } else { - Item item = items.get(0); + Item item = items.getFirst(); Component displayNameComponent = item.getDisplayName(); if (displayNameComponent != null) { String displayName = LegacyComponentSerializer.legacySection() @@ -269,7 +254,7 @@ public class LegacyPlayerListItemPacket implements MinecraftPacket { return this; } - public IdentifiedKey getPlayerKey() { + public @Nullable IdentifiedKey getPlayerKey() { return playerKey; } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PingIdentifyPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PingIdentifyPacket.java index 27c1351d..ed1e3feb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PingIdentifyPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PingIdentifyPacket.java @@ -42,6 +42,16 @@ public class PingIdentifyPacket implements MinecraftPacket { buf.writeInt(id); } + @Override + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { + return Integer.BYTES; + } + + @Override + public int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { + return Integer.BYTES; + } + @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java index ecf2887f..e779ab96 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java @@ -31,6 +31,9 @@ import org.checkerframework.checker.nullness.qual.Nullable; public class PluginMessagePacket extends DeferredByteBufHolder implements MinecraftPacket { + private static final int MAX_PAYLOAD_SIZE_CLIENTBOUND = getPayloadLimit(Direction.CLIENTBOUND); + private static final int MAX_PAYLOAD_SIZE_SERVERBOUND = getPayloadLimit(Direction.SERVERBOUND); + private @Nullable String channel; public PluginMessagePacket() { @@ -50,6 +53,19 @@ public class PluginMessagePacket extends DeferredByteBufHolder implements Minecr return channel; } + private static int getPayloadLimit(Direction direction) { + if (System.getProperty("velocity.max-plugin-message-payload-size") != null) { + return Integer.getInteger("velocity.max-plugin-message-payload-size"); + } + if (direction == Direction.SERVERBOUND) { + return Integer.getInteger("velocity.max-plugin-message-payload-size.serverbound", 32767); + } else { + // This is the vanilla expected limit, a payload this large feels like a nightmare given the trust + // we give to servers... + return Integer.getInteger("velocity.max-plugin-message-payload-size.clientbound", 1048576); + } + } + public void setChannel(String channel) { this.channel = channel; } @@ -100,6 +116,17 @@ public class PluginMessagePacket extends DeferredByteBufHolder implements Minecr } + @Override + public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + return ProtocolUtils.DEFAULT_MAX_STRING_BYTES + + (direction == Direction.CLIENTBOUND ? MAX_PAYLOAD_SIZE_CLIENTBOUND : MAX_PAYLOAD_SIZE_SERVERBOUND); + } + + @Override + public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + return 1 + 0 + 0; + } + @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemovePlayerInfoPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemovePlayerInfoPacket.java index 90ab3871..3851d278 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemovePlayerInfoPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemovePlayerInfoPacket.java @@ -17,7 +17,6 @@ package com.velocitypowered.proxy.protocol.packet; -import com.google.common.collect.Lists; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; @@ -51,7 +50,7 @@ public class RemovePlayerInfoPacket implements MinecraftPacket { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { int length = ProtocolUtils.readVarInt(buf); - Collection profilesToRemove = Lists.newArrayListWithCapacity(length); + Collection profilesToRemove = ProtocolUtils.newList(length); for (int idx = 0; idx < length; idx++) { profilesToRemove.add(ProtocolUtils.readUuid(buf)); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java index 020c3530..4d9a83d2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java @@ -80,6 +80,26 @@ public class ResourcePackResponsePacket implements MinecraftPacket { ProtocolUtils.writeVarInt(buf, status.ordinal()); } + @Override + public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { + return Long.BYTES * 2 + 1; + } else if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_9_4)) { + return ProtocolUtils.DEFAULT_MAX_STRING_BYTES + 1; + } + return 1; + } + + @Override + public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { + return Long.BYTES * 2 + 1; + } else if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_9_4)) { + return 1 + 0 + 1; + } + return 1; + } + @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); @@ -93,4 +113,4 @@ public class ResourcePackResponsePacket implements MinecraftPacket { ", status=" + status + '}'; } -} \ No newline at end of file +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCookieResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCookieResponsePacket.java index bee12b80..0731cc2d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCookieResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCookieResponsePacket.java @@ -65,6 +65,16 @@ public class ServerboundCookieResponsePacket implements MinecraftPacket { } } + @Override + public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + return ProtocolUtils.DEFAULT_MAX_STRING_BYTES + 1 + 2 + 5120; + } + + @Override + public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + return 1 + 0 + 0; + } + @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java index 6b846c23..7b2c9256 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java @@ -27,6 +27,8 @@ import io.netty.buffer.ByteBuf; public class ServerboundCustomClickActionPacket extends DeferredByteBufHolder implements MinecraftPacket { + private static final int MAX_TAG_SIZE = 65536; + public ServerboundCustomClickActionPacket() { super(null); } @@ -41,6 +43,16 @@ public class ServerboundCustomClickActionPacket extends DeferredByteBufHolder im buf.writeBytes(content()); } + @Override + public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + return ProtocolUtils.DEFAULT_MAX_STRING_BYTES + ProtocolUtils.varIntBytes(MAX_TAG_SIZE) + MAX_TAG_SIZE; + } + + @Override + public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + return 1 + 0 + 1 + 0; + } + @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/StringArgumentPropertySerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/StringArgumentPropertySerializer.java index 75f5ecc8..d4567095 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/StringArgumentPropertySerializer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/StringArgumentPropertySerializer.java @@ -37,32 +37,21 @@ class StringArgumentPropertySerializer implements ArgumentPropertySerializer StringArgumentType.word(); + case 1 -> StringArgumentType.string(); + case 2 -> StringArgumentType.greedyString(); + default -> throw new IllegalArgumentException("Invalid string argument type " + type); + }; } @Override public void serialize(StringArgumentType object, ByteBuf buf, ProtocolVersion protocolVersion) { switch (object.getType()) { - case SINGLE_WORD: - ProtocolUtils.writeVarInt(buf, 0); - break; - case QUOTABLE_PHRASE: - ProtocolUtils.writeVarInt(buf, 1); - break; - case GREEDY_PHRASE: - ProtocolUtils.writeVarInt(buf, 2); - break; - default: - throw new IllegalArgumentException("Invalid string argument type " + object.getType()); + case SINGLE_WORD -> ProtocolUtils.writeVarInt(buf, 0); + case QUOTABLE_PHRASE -> ProtocolUtils.writeVarInt(buf, 1); + case GREEDY_PHRASE -> ProtocolUtils.writeVarInt(buf, 2); + default -> throw new IllegalArgumentException("Invalid string argument type " + object.getType()); } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java index 01f36d0e..0b003326 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java @@ -113,23 +113,18 @@ public class ComponentHolder { public static BinaryTag serialize(JsonElement json) { if (json instanceof JsonPrimitive jsonPrimitive) { if (jsonPrimitive.isNumber()) { - Number number = json.getAsNumber(); + final Number number = json.getAsNumber(); - if (number instanceof Byte) { - return ByteBinaryTag.byteBinaryTag((Byte) number); - } else if (number instanceof Short) { - return ShortBinaryTag.shortBinaryTag((Short) number); - } else if (number instanceof Integer) { - return IntBinaryTag.intBinaryTag((Integer) number); - } else if (number instanceof Long) { - return LongBinaryTag.longBinaryTag((Long) number); - } else if (number instanceof Float) { - return FloatBinaryTag.floatBinaryTag((Float) number); - } else if (number instanceof Double) { - return DoubleBinaryTag.doubleBinaryTag((Double) number); - } else if (number instanceof LazilyParsedNumber) { - return IntBinaryTag.intBinaryTag(number.intValue()); - } + return switch (number) { + case Byte b -> ByteBinaryTag.byteBinaryTag(b); + case Short s -> ShortBinaryTag.shortBinaryTag(s); + case Integer i -> IntBinaryTag.intBinaryTag(i); + case Long l -> LongBinaryTag.longBinaryTag(l); + case Float f -> FloatBinaryTag.floatBinaryTag(f); + case Double d -> DoubleBinaryTag.doubleBinaryTag(d); + case LazilyParsedNumber l -> IntBinaryTag.intBinaryTag(l.intValue()); + default -> throw new IllegalArgumentException("Unknown number type: " + number); + }; } else if (jsonPrimitive.isString()) { return StringBinaryTag.stringBinaryTag(jsonPrimitive.getAsString()); } else if (jsonPrimitive.isBoolean()) { @@ -137,16 +132,16 @@ public class ComponentHolder { } else { throw new IllegalArgumentException("Unknown JSON primitive: " + jsonPrimitive); } - } else if (json instanceof JsonObject) { + } else if (json instanceof JsonObject object) { CompoundBinaryTag.Builder compound = CompoundBinaryTag.builder(); - for (Map.Entry property : ((JsonObject) json).entrySet()) { + for (Map.Entry property : object.entrySet()) { compound.put(property.getKey(), serialize(property.getValue())); } return compound.build(); - } else if (json instanceof JsonArray) { - List jsonArray = ((JsonArray) json).asList(); + } else if (json instanceof JsonArray array) { + List jsonArray = array.asList(); if (jsonArray.isEmpty()) { return ListBinaryTag.empty(); @@ -206,20 +201,21 @@ public class ComponentHolder { } public static JsonElement deserialize(BinaryTag tag) { - switch (tag.type().id()) { - case 1://BinaryTagTypes.BYTE: - return new JsonPrimitive(((ByteBinaryTag) tag).value()); - case 2://BinaryTagTypes.SHORT: - return new JsonPrimitive(((ShortBinaryTag) tag).value()); - case 3://BinaryTagTypes.INT: - return new JsonPrimitive(((IntBinaryTag) tag).value()); - case 4://BinaryTagTypes.LONG: - return new JsonPrimitive(((LongBinaryTag) tag).value()); - case 5://BinaryTagTypes.FLOAT: - return new JsonPrimitive(((FloatBinaryTag) tag).value()); - case 6://BinaryTagTypes.DOUBLE: - return new JsonPrimitive(((DoubleBinaryTag) tag).value()); - case 7://BinaryTagTypes.BYTE_ARRAY: + return switch (tag.type().id()) { + //BinaryTagTypes.BYTE + case 1 -> new JsonPrimitive(((ByteBinaryTag) tag).value()); + //BinaryTagTypes.SHORT + case 2 -> new JsonPrimitive(((ShortBinaryTag) tag).value()); + //BinaryTagTypes.INT: + case 3 -> new JsonPrimitive(((IntBinaryTag) tag).value()); + //BinaryTagTypes.LONG: + case 4 -> new JsonPrimitive(((LongBinaryTag) tag).value()); + //BinaryTagTypes.FLOAT: + case 5 -> new JsonPrimitive(((FloatBinaryTag) tag).value()); + //BinaryTagTypes.DOUBLE: + case 6 -> new JsonPrimitive(((DoubleBinaryTag) tag).value()); + //BinaryTagTypes.BYTE_ARRAY: + case 7 -> { byte[] byteArray = ((ByteArrayBinaryTag) tag).value(); JsonArray jsonByteArray = new JsonArray(byteArray.length); @@ -227,10 +223,12 @@ public class ComponentHolder { jsonByteArray.add(new JsonPrimitive(b)); } - return jsonByteArray; - case 8://BinaryTagTypes.STRING: - return new JsonPrimitive(((StringBinaryTag) tag).value()); - case 9://BinaryTagTypes.LIST: + yield jsonByteArray; + } + //BinaryTagTypes.STRING: + case 8 -> new JsonPrimitive(((StringBinaryTag) tag).value()); + //BinaryTagTypes.LIST: + case 9 -> { ListBinaryTag items = (ListBinaryTag) tag; JsonArray jsonList = new JsonArray(items.size()); @@ -238,8 +236,10 @@ public class ComponentHolder { jsonList.add(deserialize(subTag)); } - return jsonList; - case 10://BinaryTagTypes.COMPOUND: + yield jsonList; + } + //BinaryTagTypes.COMPOUND: + case 10 -> { CompoundBinaryTag compound = (CompoundBinaryTag) tag; JsonObject jsonObject = new JsonObject(); @@ -252,8 +252,10 @@ public class ComponentHolder { jsonObject.add(key.isEmpty() ? "text" : key, deserialize(compound.get(key))); }); - return jsonObject; - case 11://BinaryTagTypes.INT_ARRAY: + yield jsonObject; + } + //BinaryTagTypes.INT_ARRAY: + case 11 -> { int[] intArray = ((IntArrayBinaryTag) tag).value(); JsonArray jsonIntArray = new JsonArray(intArray.length); @@ -261,8 +263,10 @@ public class ComponentHolder { jsonIntArray.add(new JsonPrimitive(i)); } - return jsonIntArray; - case 12://BinaryTagTypes.LONG_ARRAY: + yield jsonIntArray; + } + //BinaryTagTypes.LONG_ARRAY: + case 12 -> { long[] longArray = ((LongArrayBinaryTag) tag).value(); JsonArray jsonLongArray = new JsonArray(longArray.length); @@ -270,10 +274,10 @@ public class ComponentHolder { jsonLongArray.add(new JsonPrimitive(l)); } - return jsonLongArray; - default: - throw new IllegalArgumentException("Unknown NBT tag: " + tag); - } + yield jsonLongArray; + } + default -> throw new IllegalArgumentException("Unknown NBT tag: " + tag); + }; } public static ComponentHolder read(ByteBuf buf, ProtocolVersion version) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/SystemChatPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/SystemChatPacket.java index b36014d5..1a26affd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/SystemChatPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/SystemChatPacket.java @@ -59,14 +59,9 @@ public class SystemChatPacket implements MinecraftPacket { component.write(buf); if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { switch (type) { - case SYSTEM: - buf.writeBoolean(false); - break; - case GAME_INFO: - buf.writeBoolean(true); - break; - default: - throw new IllegalArgumentException("Invalid chat type"); + case SYSTEM -> buf.writeBoolean(false); + case GAME_INFO -> buf.writeBoolean(true); + default -> throw new IllegalArgumentException("Invalid chat type"); } } else { ProtocolUtils.writeVarInt(buf, type.getId()); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java index 80b239d5..f5bb6b4f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java @@ -32,12 +32,24 @@ public class LegacyChatPacket implements MinecraftPacket { public static final byte GAME_INFO_TYPE = (byte) 2; public static final int MAX_SERVERBOUND_MESSAGE_LENGTH = 256; + private static final int MAX_SERVERBOUND_MESSAGE_LENGTH_LEGACY = getMaxServerboundMessageLength(); public static final UUID EMPTY_SENDER = new UUID(0, 0); private @Nullable String message; private byte type; private @Nullable UUID sender; + private static int getMaxServerboundMessageLength() { + final String value = System.getProperty("velocity.legacyChatMaxServerboundLength"); + if (value != null) { + try { + return Integer.parseInt(value.trim()); + } catch (final NumberFormatException e) { + } + } + return 100; + } + public LegacyChatPacket() { } @@ -92,7 +104,10 @@ public class LegacyChatPacket implements MinecraftPacket { @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { message = ProtocolUtils.readString(buf, direction == ProtocolUtils.Direction.CLIENTBOUND - ? 262144 : version.noLessThan(ProtocolVersion.MINECRAFT_1_11) ? 256 : 100); + ? 262144 + : version.noLessThan(ProtocolVersion.MINECRAFT_1_11) + ? MAX_SERVERBOUND_MESSAGE_LENGTH + : MAX_SERVERBOUND_MESSAGE_LENGTH_LEGACY); if (direction == ProtocolUtils.Direction.CLIENTBOUND && version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { type = buf.readByte(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundCustomReportDetailsPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundCustomReportDetailsPacket.java index 6a3618cb..05f535d4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundCustomReportDetailsPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundCustomReportDetailsPacket.java @@ -22,7 +22,6 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -import java.util.HashMap; import java.util.Map; public class ClientboundCustomReportDetailsPacket implements MinecraftPacket { @@ -40,7 +39,7 @@ public class ClientboundCustomReportDetailsPacket implements MinecraftPacket { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { int detailsCount = ProtocolUtils.readVarInt(buf); - this.details = new HashMap<>(detailsCount); + this.details = ProtocolUtils.newMap(detailsCount); for (int i = 0; i < detailsCount; i++) { details.put(ProtocolUtils.readString(buf), ProtocolUtils.readString(buf)); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundServerLinksPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundServerLinksPacket.java index d37866d8..e30c8a7d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundServerLinksPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundServerLinksPacket.java @@ -18,13 +18,11 @@ package com.velocitypowered.proxy.protocol.packet.config; import com.velocitypowered.api.network.ProtocolVersion; -import com.velocitypowered.api.util.ServerLink; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import io.netty.buffer.ByteBuf; -import java.util.ArrayList; import java.util.List; public class ClientboundServerLinksPacket implements MinecraftPacket { @@ -42,7 +40,7 @@ public class ClientboundServerLinksPacket implements MinecraftPacket { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { int linksCount = ProtocolUtils.readVarInt(buf); - this.serverLinks = new ArrayList<>(linksCount); + this.serverLinks = ProtocolUtils.newList(linksCount); for (int i = 0; i < linksCount; i++) { serverLinks.add(ServerLink.read(buf, version)); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java index e9811f9f..6cd338c4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java @@ -38,6 +38,11 @@ public class CodeOfConductAcceptPacket implements MinecraftPacket { public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { } + @Override + public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + return 0; + } + @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/KnownPacksPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/KnownPacksPacket.java index b3fb0de4..196e8767 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/KnownPacksPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/KnownPacksPacket.java @@ -23,6 +23,7 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.util.except.QuietDecoderException; import io.netty.buffer.ByteBuf; +import java.util.List; public class KnownPacksPacket implements MinecraftPacket { @@ -30,7 +31,7 @@ public class KnownPacksPacket implements MinecraftPacket { private static final QuietDecoderException TOO_MANY_PACKS = new QuietDecoderException("too many known packs"); - private KnownPack[] packs; + private List packs; @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, @@ -40,10 +41,10 @@ public class KnownPacksPacket implements MinecraftPacket { throw TOO_MANY_PACKS; } - final KnownPack[] packs = new KnownPack[packCount]; + final List packs = ProtocolUtils.newList(packCount); for (int i = 0; i < packCount; i++) { - packs[i] = KnownPack.read(buf); + packs.add(KnownPack.read(buf)); } this.packs = packs; @@ -52,7 +53,7 @@ public class KnownPacksPacket implements MinecraftPacket { @Override public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeVarInt(buf, packs.length); + ProtocolUtils.writeVarInt(buf, packs.size()); for (KnownPack pack : packs) { pack.write(buf); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/GenericTitlePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/GenericTitlePacket.java index d6f5e0e5..8641173e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/GenericTitlePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/GenericTitlePacket.java @@ -105,26 +105,14 @@ public abstract class GenericTitlePacket implements MinecraftPacket { public static GenericTitlePacket constructTitlePacket(ActionType type, ProtocolVersion version) { GenericTitlePacket packet = null; if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { - switch (type) { - case SET_ACTION_BAR: - packet = new TitleActionbarPacket(); - break; - case SET_SUBTITLE: - packet = new TitleSubtitlePacket(); - break; - case SET_TIMES: - packet = new TitleTimesPacket(); - break; - case SET_TITLE: - packet = new TitleTextPacket(); - break; - case HIDE: - case RESET: - packet = new TitleClearPacket(); - break; - default: - throw new IllegalArgumentException("Invalid ActionType"); - } + packet = switch (type) { + case SET_ACTION_BAR -> new TitleActionbarPacket(); + case SET_SUBTITLE -> new TitleSubtitlePacket(); + case SET_TIMES -> new TitleTimesPacket(); + case SET_TITLE -> new TitleTextPacket(); + case HIDE, RESET -> new TitleClearPacket(); + default -> throw new IllegalArgumentException("Invalid ActionType"); + }; } else { packet = new LegacyTitlePacket(); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/LegacyTitlePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/LegacyTitlePacket.java index 0425f2d3..ebae8a9e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/LegacyTitlePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/LegacyTitlePacket.java @@ -40,24 +40,19 @@ public class LegacyTitlePacket extends GenericTitlePacket { ProtocolUtils.writeVarInt(buf, getAction().getAction(version)); switch (getAction()) { - case SET_TITLE: - case SET_SUBTITLE: - case SET_ACTION_BAR: + case SET_TITLE, SET_SUBTITLE, SET_ACTION_BAR -> { if (component == null) { throw new IllegalStateException("No component found for " + getAction()); } component.write(buf); - break; - case SET_TIMES: + } + case SET_TIMES -> { buf.writeInt(fadeIn); buf.writeInt(stay); buf.writeInt(fadeOut); - break; - case HIDE: - case RESET: - break; - default: - throw new UnsupportedOperationException("Unknown action " + getAction()); + } + case HIDE, RESET -> {} + default -> throw new UnsupportedOperationException("Unknown action " + getAction()); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java index 1936eb3f..298e74d4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java @@ -230,23 +230,20 @@ public final class PluginMessageUtil { } // Before falling into the fallback, explicitly rewrite certain messages. - switch (name) { - case REGISTER_CHANNEL_LEGACY: - return REGISTER_CHANNEL; - case UNREGISTER_CHANNEL_LEGACY: - return UNREGISTER_CHANNEL; - case BRAND_CHANNEL_LEGACY: - return BRAND_CHANNEL; - case "BungeeCord": - // This is a special historical case we are compelled to support for the benefit of - // BungeeQuack. - return "bungeecord:main"; - default: + return switch (name) { + case REGISTER_CHANNEL_LEGACY -> REGISTER_CHANNEL; + case UNREGISTER_CHANNEL_LEGACY -> UNREGISTER_CHANNEL; + case BRAND_CHANNEL_LEGACY -> BRAND_CHANNEL; + // This is a special historical case we are compelled to support for the benefit of + // BungeeQuack. + case "BungeeCord" -> "bungeecord:main"; + default -> { // This is very likely a legacy name, so transform it. Velocity uses the same scheme as // BungeeCord does to transform channels, but also removes clearly invalid characters as // well. - String lower = name.toLowerCase(Locale.ROOT); - return "legacy:" + INVALID_IDENTIFIER_REGEX.matcher(lower).replaceAll(""); - } + final String lower = name.toLowerCase(Locale.ROOT); + yield "legacy:" + INVALID_IDENTIFIER_REGEX.matcher(lower).replaceAll(""); + } + }; } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/ExecutorSchedulerBackend.java b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/ExecutorSchedulerBackend.java new file mode 100644 index 00000000..d10eab6f --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/ExecutorSchedulerBackend.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018-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.scheduler; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * A {@link SchedulerBackend} backed by a real {@link ScheduledExecutorService}. + */ +public class ExecutorSchedulerBackend implements SchedulerBackend { + + private final ScheduledExecutorService executor; + + /** + * Creates a ExecutorSchedulerBackend with a default executor. + */ + public ExecutorSchedulerBackend() { + this(Executors.newSingleThreadScheduledExecutor( + new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat("Velocity Task Scheduler Timer") + .build() + )); + } + + /** + * Creates a ExecutorSchedulerBackend with a given executor. + * + * @param executor The executor to use. + */ + public ExecutorSchedulerBackend(ScheduledExecutorService executor) { + this.executor = checkNotNull(executor, "executor"); + } + + @Override + public ScheduledFuture schedule(Runnable task, long delay, TimeUnit unit) { + return executor.schedule(task, delay, unit); + } + + @Override + public ScheduledFuture scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) { + return executor.scheduleAtFixedRate(task, initialDelay, period, unit); + } + + @Override + public void shutdown() { + executor.shutdown(); + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/SchedulerBackend.java b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/SchedulerBackend.java new file mode 100644 index 00000000..ab251ca2 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/SchedulerBackend.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018-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.scheduler; + +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * Backend interface used by {@link VelocityScheduler} to schedule timer callbacks. + * + *

This is an internal abstraction that allows tests to replace the real-time scheduler + * with a deterministic implementation. + */ +interface SchedulerBackend { + + /** + * Schedules a task to run once after the given delay. + * + * @param task the task to run + * @param delay the delay + * @param unit the delay unit + * @return a future representing the scheduled task + */ + ScheduledFuture schedule(Runnable task, long delay, TimeUnit unit); + + /** + * Schedules a task to run at a fixed rate. + * + * @param task the task to run + * @param initialDelay the initial delay + * @param period the period between runs + * @param unit the time unit + * @return a future representing the scheduled task + */ + ScheduledFuture scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit); + + /** + * Shuts down the backend. + */ + void shutdown(); +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java index 727832bd..0a38582a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2023 Velocity Contributors + * Copyright (C) 2018-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 @@ -23,7 +23,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.scheduler.ScheduledTask; @@ -40,7 +39,6 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -60,20 +58,23 @@ import org.jetbrains.annotations.VisibleForTesting; public class VelocityScheduler implements Scheduler { private final PluginManager pluginManager; - private final ScheduledExecutorService timerExecutionService; + private final SchedulerBackend backend; private final Multimap tasksByPlugin = Multimaps.synchronizedMultimap( Multimaps.newSetMultimap(new IdentityHashMap<>(), HashSet::new)); /** - * Initalizes the scheduler. + * Initializes the scheduler. * * @param pluginManager the Velocity plugin manager */ public VelocityScheduler(PluginManager pluginManager) { + this(pluginManager, new ExecutorSchedulerBackend()); + } + + @VisibleForTesting + VelocityScheduler(PluginManager pluginManager, SchedulerBackend backend) { this.pluginManager = pluginManager; - this.timerExecutionService = Executors - .newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setDaemon(true) - .setNameFormat("Velocity Task Scheduler Timer").build()); + this.backend = backend; } @Override @@ -118,7 +119,7 @@ public class VelocityScheduler implements Scheduler { for (ScheduledTask task : terminating) { task.cancel(); } - timerExecutionService.shutdown(); + backend.shutdown(); final List plugins = new ArrayList<>(this.pluginManager.getPlugins()); final Iterator pluginIterator = plugins.iterator(); while (pluginIterator.hasNext()) { @@ -232,10 +233,9 @@ public class VelocityScheduler implements Scheduler { void schedule() { if (repeat == 0) { - this.future = timerExecutionService.schedule(this, delay, TimeUnit.MILLISECONDS); + this.future = backend.schedule(this, delay, TimeUnit.MILLISECONDS); } else { - this.future = timerExecutionService - .scheduleAtFixedRate(this, delay, repeat, TimeUnit.MILLISECONDS); + this.future = backend.scheduleAtFixedRate(this, delay, repeat, TimeUnit.MILLISECONDS); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java index a49d24de..037ab262 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java @@ -85,7 +85,7 @@ public class VelocityTabListLegacy extends KeyedVelocityTabList { @Override public void processLegacy(LegacyPlayerListItemPacket packet) { - Item item = packet.getItems().get(0); // Only one item per packet in 1.7 + Item item = packet.getItems().getFirst(); // Only one item per packet in 1.7 switch (packet.getAction()) { case LegacyPlayerListItemPacket.ADD_PLAYER: diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java index d8cdb7c3..59aae3e4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java @@ -145,45 +145,44 @@ public enum InformationUtils { * @return {@link String} address with public parts redacted */ public static String anonymizeInetAddress(InetAddress address) { - if (address instanceof Inet4Address) { - Inet4Address v4 = (Inet4Address) address; - if (v4.isAnyLocalAddress() || v4.isLoopbackAddress() - || v4.isLinkLocalAddress() - || v4.isSiteLocalAddress()) { - return address.getHostAddress(); - } else { - byte[] addr = v4.getAddress(); - return (addr[0] & 0xff) + "." + (addr[1] & 0xff) + ".XXX.XXX"; - } - } else if (address instanceof Inet6Address) { - Inet6Address v6 = (Inet6Address) address; - if (v6.isAnyLocalAddress() || v6.isLoopbackAddress() - || v6.isSiteLocalAddress() - || v6.isSiteLocalAddress()) { - return address.getHostAddress(); - } else { - String[] bits = v6.getHostAddress().split(":"); - String ret = ""; - boolean flag = false; - for (int iter = 0; iter < bits.length; iter++) { - if (flag) { - ret += ":X"; - continue; - } - if (!bits[iter].equals("0")) { - if (iter == 0) { - ret = bits[iter]; - } else { - ret = "::" + bits[iter]; - } - flag = true; - } + return switch (address) { + case Inet4Address v4 -> { + if (v4.isAnyLocalAddress() || v4.isLoopbackAddress() + || v4.isLinkLocalAddress() + || v4.isSiteLocalAddress()) { + yield address.getHostAddress(); + } else { + byte[] addr = v4.getAddress(); + yield (addr[0] & 0xff) + "." + (addr[1] & 0xff) + ".XXX.XXX"; + } + } case Inet6Address v6 -> { + if (v6.isAnyLocalAddress() || v6.isLoopbackAddress() + || v6.isSiteLocalAddress() + || v6.isSiteLocalAddress()) { + yield address.getHostAddress(); + } else { + String[] bits = v6.getHostAddress().split(":"); + String ret = ""; + boolean flag = false; + for (int iter = 0; iter < bits.length; iter++) { + if (flag) { + ret += ":X"; + continue; + } + if (!bits[iter].equals("0")) { + if (iter == 0) { + ret = bits[iter]; + } else { + ret = "::" + bits[iter]; + } + flag = true; + } + } + yield ret; } - return ret; } - } else { - return address.getHostAddress(); - } + default -> address.getHostAddress(); + }; } /** diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/IntervalledCounter.java b/proxy/src/main/java/com/velocitypowered/proxy/util/IntervalledCounter.java new file mode 100644 index 00000000..9b986dca --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/IntervalledCounter.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2025 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.util; + +/** + * IntervalledCounter maintains a rolling sum of values associated with timestamps, keeping + * only those entries that fall within a fixed time interval from the most recent timestamp. + * + *

Time values must be provided in the same unit as {@link System#nanoTime()} (nanoseconds), + * and the configured interval is also expressed in nanoseconds. Callers are expected to + * periodically advance the counter to the current time using {@link #updateCurrentTime()} or + * {@link #updateCurrentTime(long)} to evict expired entries before adding new ones via + * {@link #addTime(long)} or {@link #addTime(long, long)}.

+ * + *

This class is not thread-safe. If multiple threads access an instance concurrently, + * external synchronization is required.

+ */ +@SuppressWarnings("checkstyle:WhitespaceAfter") // Not our class +public final class IntervalledCounter { + + private static final int INITIAL_SIZE = 8; + + /** + * Ring buffer holding the timestamp (in nanoseconds) for each data point. + */ + protected long[] times; + /** + * Ring buffer holding the count associated with each timestamp. + */ + protected long[] counts; + /** + * The sliding window size in nanoseconds. Only entries with time >= (currentTime - interval) + * are considered part of the window. + */ + protected final long interval; + /** + * Cached lower bound of the window (in nanoseconds) after the last update. + */ + protected long minTime; + /** + * Running sum of all counts currently within the window. + */ + protected long sum; + /** + * Head index (inclusive) of the ring buffer. + */ + protected int head; // inclusive + /** + * Tail index (exclusive) of the ring buffer. + */ + protected int tail; // exclusive + + /** + * Creates a new counter with the specified interval. + * + * @param interval the window size in nanoseconds (compatible with {@link System#nanoTime()}) + */ + public IntervalledCounter(final long interval) { + this.times = new long[INITIAL_SIZE]; + this.counts = new long[INITIAL_SIZE]; + this.interval = interval; + } + + /** + * Advances the window to the current time using {@link System#nanoTime()}, evicting any + * data points that have fallen outside of the interval and updating the running sum. + */ + public void updateCurrentTime() { + this.updateCurrentTime(System.nanoTime()); + } + + /** + * Advances the window to the provided time, evicting any data points older than + * {@code currentTime - interval} and updating the running sum. + * + * @param currentTime the current time in nanoseconds (as from {@link System#nanoTime()}) + */ + public void updateCurrentTime(final long currentTime) { + long sum = this.sum; + int head = this.head; + final int tail = this.tail; + final long minTime = currentTime - this.interval; + + final int arrayLen = this.times.length; + + // guard against overflow by using subtraction + while (head != tail && this.times[head] - minTime < 0) { + sum -= this.counts[head]; + // there are two ways we can do this: + // 1. free the count when adding + // 2. free it now + // option #2 + this.counts[head] = 0; + if (++head >= arrayLen) { + head = 0; + } + } + + this.sum = sum; + this.head = head; + this.minTime = minTime; + } + + /** + * Adds a single unit at the specified timestamp, assuming the timestamp is within the current + * window. If the timestamp is older than the current window lower bound, the value is ignored. + * This method does not automatically advance the window; callers should invoke + * {@link #updateCurrentTime()} or {@link #updateCurrentTime(long)} beforehand. + * + * @param currTime the timestamp in nanoseconds + */ + public void addTime(final long currTime) { + this.addTime(currTime, 1L); + } + + /** + * Adds {@code count} units at the specified timestamp, assuming the timestamp is within the + * current window. If the timestamp is older than {@code minTime}, the value is ignored. + * This method does not automatically advance the window; callers should invoke + * {@link #updateCurrentTime()} or {@link #updateCurrentTime(long)} beforehand. + * + * @param currTime the timestamp in nanoseconds + * @param count the amount to add (non-negative) + */ + public void addTime(final long currTime, final long count) { + // guard against overflow by using subtraction + if (currTime - this.minTime < 0) { + return; + } + int nextTail = (this.tail + 1) % this.times.length; + if (nextTail == this.head) { + this.resize(); + nextTail = (this.tail + 1) % this.times.length; + } + + this.times[this.tail] = currTime; + this.counts[this.tail] += count; + this.sum += count; + this.tail = nextTail; + } + + /** + * Convenience method that advances the window to the current time and then adds {@code count} + * units at that time. + * + * @param count the amount to add (non-negative) + */ + public void updateAndAdd(final long count) { + final long currTime = System.nanoTime(); + this.updateCurrentTime(currTime); + this.addTime(currTime, count); + } + + /** + * Convenience method that advances the window to {@code currTime} and then adds {@code count} + * units at that time. + * + * @param count the amount to add (non-negative) + * @param currTime the timestamp in nanoseconds + */ + public void updateAndAdd(final long count, final long currTime) { + this.updateCurrentTime(currTime); + this.addTime(currTime, count); + } + + /** + * Doubles the capacity of the internal ring buffers, preserving the order of existing data. + */ + private void resize() { + final long[] oldElements = this.times; + final long[] oldCounts = this.counts; + final long[] newElements = new long[this.times.length * 2]; + final long[] newCounts = new long[this.times.length * 2]; + this.times = newElements; + this.counts = newCounts; + + final int head = this.head; + final int tail = this.tail; + final int size = tail >= head ? (tail - head) : (tail + (oldElements.length - head)); + this.head = 0; + this.tail = size; + + if (tail >= head) { + // sequentially ordered from [head, tail) + System.arraycopy(oldElements, head, newElements, 0, size); + System.arraycopy(oldCounts, head, newCounts, 0, size); + } else { + // ordered from [head, length) + // then followed by [0, tail) + + System.arraycopy(oldElements, head, newElements, 0, oldElements.length - head); + System.arraycopy(oldElements, 0, newElements, oldElements.length - head, tail); + + System.arraycopy(oldCounts, head, newCounts, 0, oldCounts.length - head); + System.arraycopy(oldCounts, 0, newCounts, oldCounts.length - head, tail); + } + } + + /** + * Returns the current rate in units per second based on the rolling sum and the configured + * interval. Specifically: {@code sum / (intervalSeconds)} where {@code intervalSeconds} + * equals {@code interval / 1e9}. + * + * @return the rate in units per second for the current window + */ + public double getRate() { + return (double)this.sum / ((double)this.interval * 1.0E-9); + } + + /** + * Returns the configured interval size in nanoseconds. + * + * @return the interval size in nanoseconds + */ + public long getInterval() { + return this.interval; + } + + /** + * Returns the rolling sum of all counts currently within the window. + * + * @return the rolling sum + */ + public long getSum() { + return this.sum; + } + + /** + * Returns the number of data points currently stored in the internal ring buffer. This may be + * less than or equal to the number of points added since older entries may have been evicted. + * + * @return the number of stored data points + */ + public int totalDataPoints() { + return this.tail >= this.head ? (this.tail - this.head) : (this.tail + (this.counts.length - this.head)); + } +} diff --git a/proxy/log4j2-plugin/src/main/java/com/velocitypowered/proxy/util/StripAnsiConverter.java b/proxy/src/main/java/com/velocitypowered/proxy/util/StripAnsiConverter.java similarity index 100% rename from proxy/log4j2-plugin/src/main/java/com/velocitypowered/proxy/util/StripAnsiConverter.java rename to proxy/src/main/java/com/velocitypowered/proxy/util/StripAnsiConverter.java diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/TranslatableMapper.java b/proxy/src/main/java/com/velocitypowered/proxy/util/TranslatableMapper.java index 6f30d3d1..20ab4329 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/TranslatableMapper.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/TranslatableMapper.java @@ -43,6 +43,12 @@ public enum TranslatableMapper implements BiConsumer: +velocity.error.connecting-server-error=Unable to connect you to . Please try again later. +velocity.error.connected-server-error=Your connection to encountered a problem. velocity.error.internal-server-connection-error=An internal server connection error occurred. velocity.error.logging-in-too-fast=You are logging in too fast, try again later. velocity.error.online-mode-only=You are not logged into your Minecraft account. If you are logged into your Minecraft account, try restarting your Minecraft client. velocity.error.player-connection-error=An internal error occurred in your connection. velocity.error.modern-forwarding-needs-new-client=This server is only compatible with Minecraft 1.13 and above. velocity.error.modern-forwarding-failed=Your server did not send a forwarding request to the proxy. Make sure the server is configured for Velocity forwarding. -velocity.error.moved-to-new-server=You were kicked from {0}: {1} +velocity.error.moved-to-new-server=You were kicked from : velocity.error.no-available-servers=There are no available servers to connect you to. Try again later or contact an admin. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=An error occurred while running this command. velocity.command.command-does-not-exist=This command does not exist. velocity.command.players-only=Only players can run this command. -velocity.command.server-does-not-exist=The specified server {0} does not exist. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=You are currently connected to {0}. +velocity.command.server-does-not-exist=The specified server does not exist. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=You are currently connected to . velocity.command.server-too-many=There are too many servers set up. Use tab completion to view all servers available. velocity.command.server-available=Available servers: -velocity.command.server-tooltip-player-online={0} player online -velocity.command.server-tooltip-players-online={0} players online +velocity.command.server-tooltip-player-online= player online +velocity.command.server-tooltip-players-online= players online velocity.command.server-tooltip-current-server=Currently connected to this server velocity.command.server-tooltip-offer-connect-server=Click to connect to this server -velocity.command.glist-player-singular={0} player is currently connected to the proxy. -velocity.command.glist-player-plural={0} players are currently connected to the proxy. +velocity.command.glist-player-singular= player is currently connected to the proxy. +velocity.command.glist-player-plural= players are currently connected to the proxy. velocity.command.glist-view-all=To view all players on servers, use /glist all. velocity.command.reload-success=Velocity configuration successfully reloaded. velocity.command.reload-failure=Unable to reload your Velocity configuration. Check the console for more details. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=There are no plugins currently installed. -velocity.command.plugins-list=Plugins: {0} -velocity.command.plugin-tooltip-website=Website: {0} -velocity.command.plugin-tooltip-author=Author: {0} -velocity.command.plugin-tooltip-authors=Authors: {0} +velocity.command.plugins-list=Plugins: +velocity.command.plugin-tooltip-website=Website: +velocity.command.plugin-tooltip-author=Author: +velocity.command.plugin-tooltip-authors=Authors: velocity.command.dump-uploading=Uploading gathered information... velocity.command.dump-send-error=An error occurred while communicating with the Velocity servers. The servers may be temporarily unavailable or there is an issue with your network settings. You can find more information in the log or console of your Velocity server. velocity.command.dump-success=Created an anonymised report containing useful information about this proxy. If a developer requested it, you may share the following link with them: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ar_SA.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ar_SA.properties index 8f79f570..c93bbb46 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ar_SA.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ar_SA.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=أنت بالفعل متصل بهذا السيرفر\! velocity.error.already-connected-proxy=أنت بالفعل متصل بهذا الوكيل\! velocity.error.already-connecting=أنت بالفعل تحاول الاتصال بأحد السيرفرات\! -velocity.error.cant-connect=فشل الاتصال بـ{0}\: {1} -velocity.error.connecting-server-error=فشل الاتصال بـ{0}، حاول في وقتٍ لاحق. -velocity.error.connected-server-error=واجه اتصالك بـ{0} مشكلة. +velocity.error.cant-connect=فشل الاتصال بـ\: +velocity.error.connecting-server-error=فشل الاتصال بـ، حاول في وقتٍ لاحق. +velocity.error.connected-server-error=واجه اتصالك بـ مشكلة. velocity.error.internal-server-connection-error=حدث خطأ في الاتصال بالسيرفر الداخلي. velocity.error.logging-in-too-fast=لقد حاولت تسجيل الدخول كثيرًا مؤخرًا، حاول في وقتٍ لاحق. velocity.error.online-mode-only=لم تقم بتسجيل الدخول بحساب ماينكرافت. إذا كنت مسجل بالفعل جرب إعادة تشغيل اللعبة. velocity.error.player-connection-error=حدث خطأ داخلي في الاتصال الخاص بك. velocity.error.modern-forwarding-needs-new-client=هذا السيرفر متوافق فقط مع ماينكرافت 1.13 و ما فوق. velocity.error.modern-forwarding-failed=السيرفر الخاص بك لم يرسل طلب إعادة توجيه إلى الوكيل. تأكد من إعداد الخادم لإعادة التوجيه بـVelocity. -velocity.error.moved-to-new-server=لقد تم طردك من {0}\: {1} +velocity.error.moved-to-new-server=لقد تم طردك من \: velocity.error.no-available-servers=لا توجد سيرفرات متاحة للاتصال. حاول مرة أخرى أو اتصل بالأدمِن. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=حدث خطأ أثناء تنفيذ هذا الأمر. velocity.command.command-does-not-exist=هذا الأمر غير موجود. velocity.command.players-only=يمكن للاعبين فقط تشغيل هذا الأمر. -velocity.command.server-does-not-exist=السيرفر المطلوب {0} غير موجود. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=أنت الآن متصل بـ{0} +velocity.command.server-does-not-exist=السيرفر المطلوب غير موجود. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=أنت الآن متصل بـ velocity.command.server-too-many=هناك العديد من السيرفرات المتاحة، استخدم البحث بزر tab لتصفح قائمة السيرفرات. velocity.command.server-available=السيرفرات المتاحة\: velocity.command.server-tooltip-player-online=لاعب واحد متصل -velocity.command.server-tooltip-players-online={0} لاعبين متصلون +velocity.command.server-tooltip-players-online= لاعبين متصلون velocity.command.server-tooltip-current-server=انت متصل حاليًا بهذا السيرفر velocity.command.server-tooltip-offer-connect-server=انقر للاتصال بهذا السيرفر velocity.command.glist-player-singular=هناك لاعب واحد متصل بالوكيل. -velocity.command.glist-player-plural=هناك {0} لاعبين متصلون بالوكيل. +velocity.command.glist-player-plural=هناك لاعبين متصلون بالوكيل. velocity.command.glist-view-all=لعرض اللاعبين على جميع السيرفرات استخدم /glist all velocity.command.reload-success=تم إعادة تحميل إعدادات Velocity بنجاح. velocity.command.reload-failure=فشلت إعادة تحميل إعدادات Velocity. تفقد الـconsole للمزيد من التفاصيل. -velocity.command.version-copyright=حقوق الطبع والنشر 2018-{2} {0}. {1} مرخصة بموجب شروط الإصدار الثالث لرخصة GNU العامة (GPLv3). +velocity.command.version-copyright=حقوق الطبع والنشر 2018- . مرخصة بموجب شروط الإصدار الثالث لرخصة GNU العامة (GPLv3). velocity.command.no-plugins=لا توجد إضافات مثبتة على Velocity. -velocity.command.plugins-list=الإضافات\: {0} -velocity.command.plugin-tooltip-website=موقعها\: {0} -velocity.command.plugin-tooltip-author=تصميم\: {0} -velocity.command.plugin-tooltip-authors=تصميم\: {0} +velocity.command.plugins-list=الإضافات\: +velocity.command.plugin-tooltip-website=موقعها\: +velocity.command.plugin-tooltip-author=تصميم\: +velocity.command.plugin-tooltip-authors=تصميم\: velocity.command.dump-uploading=جاري تجميع و رفع معلومات نظامك... velocity.command.dump-send-error=حدث خطأ أثناء الاتصال بسيرفر Velocity. قد يكون السيرفر غير متاح مؤقتاً أو هناك مشكلة في إعدادات الشبكة الخاصة بك. يمكنك العثور على مزيد من المعلومات في log أو console وكيل Velocity الخاص بك. velocity.command.dump-success=تم إنشاء تقرير مفصل يحتوي على معلومات مفيدة عن الوكيل الخاص بك. إذا طلبه المطور، يمكنك مشاركة الرابط التالي معه\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_bg_BG.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_bg_BG.properties index c554fa3d..792065f2 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_bg_BG.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_bg_BG.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Вече сте свързани към този сървър\! velocity.error.already-connected-proxy=Вече сте свързани към това прокси\! velocity.error.already-connecting=Вече се опитвате да се свържете към сървър\! -velocity.error.cant-connect=Не успяхме да Ви свържем към {0}\: {1} -velocity.error.connecting-server-error=Не успяхме да Ви свържем към {0}. Моля, опитайте по-късно. -velocity.error.connected-server-error=Възникна грешка, докато бяхте свързан към {0}. +velocity.error.cant-connect=Не успяхме да Ви свържем към \: +velocity.error.connecting-server-error=Не успяхме да Ви свържем към . Моля, опитайте по-късно. +velocity.error.connected-server-error=Възникна грешка, докато бяхте свързан към . velocity.error.internal-server-connection-error=Възникна вътрешна грешка със сървърната връзка. velocity.error.logging-in-too-fast=Опитвате се да влизате твърде бързо - моля, опитайте по-късно. velocity.error.online-mode-only=Не сте влезли в своя Minecraft акаунт. Ако вече сте го направили, опитайте да рестартирате играта и лаунчера и опитайте отново. velocity.error.player-connection-error=Възникна грешка с вашата връзка. velocity.error.modern-forwarding-needs-new-client=Този сървър е съвместим само с Minecraft 1.13 или по-нова версия. velocity.error.modern-forwarding-failed=Сървъра Ви не изпрати заявка за препращане на информация към проксито. Моля, убедете се, че сървъра Ви е настроен за работа с Velocity. -velocity.error.moved-to-new-server=Ти беше изхвърлен от {0}\: {1} +velocity.error.moved-to-new-server=Ти беше изхвърлен от \: velocity.error.no-available-servers=Няма налични сървъри, км които да Ви свържем. Моля, опитайте по-късно, или се свържете с администратор. velocity.error.illegal-chat-characters=Неодобрени символи в чата # Commands velocity.command.generic-error=Възникна грешка при изпълняването на командата. velocity.command.command-does-not-exist=Тази команда не съществува. velocity.command.players-only=Само играчи могат да изпълняват тази команда. -velocity.command.server-does-not-exist=Сървър с името {0} не съществува. -velocity.command.player-not-found=Играч с името {0} не съществува. -velocity.command.server-current-server=В момента сте свързан към {0}. +velocity.command.server-does-not-exist=Сървър с името не съществува. +velocity.command.player-not-found=Играч с името не съществува. +velocity.command.server-current-server=В момента сте свързан към . velocity.command.server-too-many=Има прекалено много регистрирани сървъри. Използвайте TAB, за да видите всички налични сървъри. velocity.command.server-available=Налични сървъри\: -velocity.command.server-tooltip-player-online={0} играч на линия -velocity.command.server-tooltip-players-online={0} играчи на линия +velocity.command.server-tooltip-player-online= играч на линия +velocity.command.server-tooltip-players-online= играчи на линия velocity.command.server-tooltip-current-server=В момента сте свързани към този сървър velocity.command.server-tooltip-offer-connect-server=Натиснете тук, за да Ви свържем към този сървър -velocity.command.glist-player-singular={0} играч е свързан към проксито. -velocity.command.glist-player-plural={0} играчи са свързани към проксито. +velocity.command.glist-player-singular= играч е свързан към проксито. +velocity.command.glist-player-plural= играчи са свързани към проксито. velocity.command.glist-view-all=За да видите всички играчи, разпределени по сървъри, използвайте /glist all. velocity.command.reload-success=Настройките на Velocity бяха презаредени успешно. velocity.command.reload-failure=Не успяхме да презаредим настройките на Velocity. Моля, проверете конзолата за повече информация. -velocity.command.version-copyright=Авторско право 2018-{2} {0}. {1} е лицензиран под условията на GNU General Public License v3. +velocity.command.version-copyright=Авторско право 2018- . е лицензиран под условията на GNU General Public License v3. velocity.command.no-plugins=За момента няма инсталирани добавки. -velocity.command.plugins-list=Добавки\: {0} -velocity.command.plugin-tooltip-website=Уебсайт\: {0} -velocity.command.plugin-tooltip-author=Автор\: {0} -velocity.command.plugin-tooltip-authors=Автори\: {0} +velocity.command.plugins-list=Добавки\: +velocity.command.plugin-tooltip-website=Уебсайт\: +velocity.command.plugin-tooltip-author=Автор\: +velocity.command.plugin-tooltip-authors=Автори\: velocity.command.dump-uploading=Качваме събраната информация... velocity.command.dump-send-error=Възникна грешка при комуникацията със сървърите на Velocity. Сървърите може временно да не са налични, или да имате проблем с мрежовите настройки. Ще откриете повече информация в логовете или конзолата на Вашето Velocity прокси. velocity.command.dump-success=Създадохме анонимен доклад, съдържащ полезна информация относно това прокси. Ако разработчик Ви го е поискал, може да споделите този линк с тях\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_cs_CZ.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_cs_CZ.properties index 980e4591..336211c1 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_cs_CZ.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_cs_CZ.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=K tomuto serveru jsi již připojen\! velocity.error.already-connected-proxy=K tomuto proxy serveru jsi již připojen\! velocity.error.already-connecting=Již se pokoušíš o připojení k serveru\! -velocity.error.cant-connect=Nepodařilo se připojit k serveru {0}\: {1} -velocity.error.connecting-server-error=Nepodařilo se tě připojit k serveru {0}. Zkus to prosím později. -velocity.error.connected-server-error=Nastala chyba ve tvém připojení k serveru {0}. +velocity.error.cant-connect=Nepodařilo se připojit k serveru \: +velocity.error.connecting-server-error=Nepodařilo se tě připojit k serveru . Zkus to prosím později. +velocity.error.connected-server-error=Nastala chyba ve tvém připojení k serveru . velocity.error.internal-server-connection-error=V připojení k serveru se vyskytla interní chyba. velocity.error.logging-in-too-fast=Přihlašuješ se příliš rychle, počkej chvíli. velocity.error.online-mode-only=Nejsi připojen ke svému Minecraft účtu. Pokud ano, nastala chyba. Zkus restartovat hru. velocity.error.player-connection-error=Ve tvém připojení nastala chyba. velocity.error.modern-forwarding-needs-new-client=Tento server je kompatibilní pouze s verzí Minecraftu 1.13 a vyšší. velocity.error.modern-forwarding-failed=Tvůj server neodeslal přesměrovávací požadavek na proxy server. Ujisti se, že je server nastaven na Velocity přesměrování. -velocity.error.moved-to-new-server=Byl jsi vyhozen ze serveru {0}\: {1} +velocity.error.moved-to-new-server=Byl jsi vyhozen ze serveru \: velocity.error.no-available-servers=Nejsou k dispozici žádné servery, ke kterým by ses mohl připojit. Zkus to později nebo kontaktuj správce. velocity.error.illegal-chat-characters=Nepovolené znaky v chatu # Commands velocity.command.generic-error=Při vykonávání tohoto příkazu nastala chyba. velocity.command.command-does-not-exist=Tento příkaz neexistuje. velocity.command.players-only=Tento příkaz mohou vykonávat pouze hráči. -velocity.command.server-does-not-exist=Zadaný server {0} neexistuje. -velocity.command.player-not-found=Zadaný hráč {0} neexistuje. -velocity.command.server-current-server=Právě jsi připojen k serveru {0}. +velocity.command.server-does-not-exist=Zadaný server neexistuje. +velocity.command.player-not-found=Zadaný hráč neexistuje. +velocity.command.server-current-server=Právě jsi připojen k serveru . velocity.command.server-too-many=Je nastaveno příliš mnoho serverů. Klávesa tab ukáže všechny dostupné servery. velocity.command.server-available=Dostupné servery\: -velocity.command.server-tooltip-player-online={0} hráč online -velocity.command.server-tooltip-players-online=Počet hráčů online\: {0} +velocity.command.server-tooltip-player-online= hráč online +velocity.command.server-tooltip-players-online=Počet hráčů online\: velocity.command.server-tooltip-current-server=Právě jsi připojen k tomuto serveru velocity.command.server-tooltip-offer-connect-server=Kliknutím se připojíš k tomuto serveru -velocity.command.glist-player-singular=K tomuto proxy serveru je připojen {0} hráč. -velocity.command.glist-player-plural=Počet hráčů připojených k tomuto proxy serveru\: {0} +velocity.command.glist-player-singular=K tomuto proxy serveru je připojen hráč. +velocity.command.glist-player-plural=Počet hráčů připojených k tomuto proxy serveru\: velocity.command.glist-view-all=Ke zobrazení všech hráčů na všech serverech použij /glist all. velocity.command.reload-success=Konfigurace Velocity úspěšně načtena. velocity.command.reload-failure=Nebylo možné načíst konfiguraci Velocity. Podrobnosti jsou na konzoli. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} je licencovaný pod podmínkami GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . je licencovaný pod podmínkami GNU General Public License v3. velocity.command.no-plugins=V tuto chvíli nejsou nainstalovány žádné zásuvné moduly. -velocity.command.plugins-list=Zásuvné moduly\: {0} -velocity.command.plugin-tooltip-website=Webová stránka\: {0} -velocity.command.plugin-tooltip-author=Autor\: {0} -velocity.command.plugin-tooltip-authors=Autoři\: {0} +velocity.command.plugins-list=Zásuvné moduly\: +velocity.command.plugin-tooltip-website=Webová stránka\: +velocity.command.plugin-tooltip-author=Autor\: +velocity.command.plugin-tooltip-authors=Autoři\: velocity.command.dump-uploading=Nahrávání získaných informací... velocity.command.dump-send-error=Nastala chyba při komunikaci s Velocity servery. Servery mohou být dočasně nedostupné nebo je chyba v přístupu na internet. Podrobnosti jsou v logu a na konzoli Velocity serveru. velocity.command.dump-success=Byla vytvořena anonymizovaná zpráva obsahující užitečné informace o tomto serveru. Vyžádal-li si je vývojář, můžeš mu poslat nasledující odkaz\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_da_DK.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_da_DK.properties index 1a82cbdf..4d80e07f 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_da_DK.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_da_DK.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Du er allerede tilsluttet til den server\! velocity.error.already-connected-proxy=Du er allerede tilsluttet til proxyen\! velocity.error.already-connecting=Du forsøger allerede at oprette forbindelse til en server\! -velocity.error.cant-connect=Kan ikke forbinde til {0}\: {1} -velocity.error.connecting-server-error=Kan ikke forbinde dig til {0}. Prøv igen senere. -velocity.error.connected-server-error=Din forbindelse til {0} stødte på et problem. +velocity.error.cant-connect=Kan ikke forbinde til \: +velocity.error.connecting-server-error=Kan ikke forbinde dig til . Prøv igen senere. +velocity.error.connected-server-error=Din forbindelse til stødte på et problem. velocity.error.internal-server-connection-error=Der opstod en intern server forbindelsesfejl. velocity.error.logging-in-too-fast=Du logger ind for hurtigt, prøv igen senere. velocity.error.online-mode-only=Du er ikke logget ind på din Minecraft-konto. Hvis du er logget ind på din Minecraft-konto, så prøv at genstarte din Minecraft-klient. velocity.error.player-connection-error=Der opstod en intern fejl i din forbindelse. velocity.error.modern-forwarding-needs-new-client=Denne server er kun kompatibel med Minecraft 1.13 og derover. velocity.error.modern-forwarding-failed=Din server sendte ikke en viderestillingsanmodning til proxyen. Sørg for, at serveren er konfigureret til Velocity forwarding. -velocity.error.moved-to-new-server=Du blev smidt ud fra {0}\: {1} +velocity.error.moved-to-new-server=Du blev smidt ud fra \: velocity.error.no-available-servers=Der er ingen tilgængelige servere at forbinde dig til. Prøv igen senere eller kontakt en administrator. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=Der opstod en fejl da du kørte kommandoen. velocity.command.command-does-not-exist=Denne kommando eksisterer ikke. velocity.command.players-only=Kun spillere kan køre denne kommando. -velocity.command.server-does-not-exist=Den angivne server {0} findes ikke. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Du er i øjeblikket forbundet til {0}. +velocity.command.server-does-not-exist=Den angivne server findes ikke. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Du er i øjeblikket forbundet til . velocity.command.server-too-many=Der er sat for mange servere op. Brug tab færdiggørelse til at se alle tilgængelige servere. velocity.command.server-available=Tilgængelige servere\: -velocity.command.server-tooltip-player-online={0} spiller online -velocity.command.server-tooltip-players-online={0} spillere online +velocity.command.server-tooltip-player-online= spiller online +velocity.command.server-tooltip-players-online= spillere online velocity.command.server-tooltip-current-server=I øjeblikket forbundet til serveren velocity.command.server-tooltip-offer-connect-server=Klik for at forbinde til denne server -velocity.command.glist-player-singular={0} spiller er i øjeblikket forbundet til proxyen. -velocity.command.glist-player-plural={0} spillere er i øjeblikket forbundet til proxyen. +velocity.command.glist-player-singular= spiller er i øjeblikket forbundet til proxyen. +velocity.command.glist-player-plural= spillere er i øjeblikket forbundet til proxyen. velocity.command.glist-view-all=For at se alle spillere på servere, brug /glist all. velocity.command.reload-success=Velocity konfiguration blev genindlæst. velocity.command.reload-failure=Kan ikke genindlæse din Velocity konfiguration. Tjek konsollen for flere detaljer. -velocity.command.version-copyright=Ophavsret 2018-{2} {0}. {1} er licenseret under betingelserne i GNU General Public License v3. +velocity.command.version-copyright=Ophavsret 2018- . er licenseret under betingelserne i GNU General Public License v3. velocity.command.no-plugins=Der er ingen plugins installeret i øjeblikket. -velocity.command.plugins-list=Plugins\: {0} -velocity.command.plugin-tooltip-website=Hjemmeside\: {0} -velocity.command.plugin-tooltip-author=Forfatter\: {0} -velocity.command.plugin-tooltip-authors=Skabere\: {0} +velocity.command.plugins-list=Plugins\: +velocity.command.plugin-tooltip-website=Hjemmeside\: +velocity.command.plugin-tooltip-author=Forfatter\: +velocity.command.plugin-tooltip-authors=Skabere\: velocity.command.dump-uploading=Uploader indsamlet information... velocity.command.dump-send-error=Der opstod en fejl under kommunikation med Velocity serverne. Serverne kan være midlertidigt utilgængelige, eller der er et problem med dine netværksindstillinger. Du kan finde mere information i loggen eller konsollen på din Velocity server. velocity.command.dump-success=Oprettet en anonymiseret rapport med nyttige oplysninger om denne proxy. Hvis en udvikler anmodede om det, kan du dele følgende link med dem\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_de_DE.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_de_DE.properties index 4de00f9e..65004a1d 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_de_DE.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_de_DE.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Du bist bereits mit diesem Server verbunden\! velocity.error.already-connected-proxy=Du bist bereits mit diesem Proxy verbunden\! velocity.error.already-connecting=Du versuchst dich bereits mit einem Server zu verbinden\! -velocity.error.cant-connect=Kein Verbindungsaufbau zu {0} möglich\: {1} -velocity.error.connecting-server-error=Kein Verbindungsaufbau zu {0} möglich. Bitte versuche es später erneut. -velocity.error.connected-server-error=Bei der Verbindung zu {0} ist ein Problem aufgetreten. +velocity.error.cant-connect=Kein Verbindungsaufbau zu möglich\: +velocity.error.connecting-server-error=Kein Verbindungsaufbau zu möglich. Bitte versuche es später erneut. +velocity.error.connected-server-error=Bei der Verbindung zu ist ein Problem aufgetreten. velocity.error.internal-server-connection-error=Bei der Verbindung mit dem Server ist ein interner Fehler aufgetreten. velocity.error.logging-in-too-fast=Du meldest dich zu schnell an, versuche es später noch einmal. velocity.error.online-mode-only=Du bist nicht in deinem Minecraft Konto eingeloggt. Wenn du in deinem Minecraft Konto eingeloggt bist, versuche deinen Minecraft Client neu zu starten. velocity.error.player-connection-error=Bei deiner Verbindung ist ein interner Fehler aufgetreten. velocity.error.modern-forwarding-needs-new-client=Dieser Server ist nur mit der Minecraft Version 1.13 und höher kompatibel. velocity.error.modern-forwarding-failed=Dein Server hat keine Weiterleitungsanforderung an den Proxy gesendet. Stelle sicher, dass der Server für die Velocity Weiterleitung konfiguriert ist. -velocity.error.moved-to-new-server=Du wurdest von {0} vom Server geworfen\: {1} +velocity.error.moved-to-new-server=Du wurdest von vom Server geworfen\: velocity.error.no-available-servers=Es gibt keine verfügbaren Server mit denen du dich verbinden kannst. Versuche es später erneut oder kontaktiere einen Admin. velocity.error.illegal-chat-characters=Ungültige Zeichen im Chat # Commands velocity.command.generic-error=Beim Ausführen des Befehls ist ein Fehler aufgetreten. velocity.command.command-does-not-exist=Dieser Befehl existiert nicht. velocity.command.players-only=Nur Spieler können diesen Befehl ausführen. -velocity.command.server-does-not-exist=Der angegebene Server {0} existiert nicht. -velocity.command.player-not-found=Der angegebene Spieler {0} existiert nicht. -velocity.command.server-current-server=Du bist derzeit mit {0} verbunden. +velocity.command.server-does-not-exist=Der angegebene Server existiert nicht. +velocity.command.player-not-found=Der angegebene Spieler existiert nicht. +velocity.command.server-current-server=Du bist derzeit mit verbunden. velocity.command.server-too-many=Es sind zu viele Server eingerichtet. Verwende die Tabvervollständigung, um alle verfügbaren Server aufzulisten. velocity.command.server-available=Verfügbare Server\: -velocity.command.server-tooltip-player-online={0} Spieler online -velocity.command.server-tooltip-players-online={0} Spieler online +velocity.command.server-tooltip-player-online= Spieler online +velocity.command.server-tooltip-players-online= Spieler online velocity.command.server-tooltip-current-server=Du bist derzeit mit diesem Server verbunden velocity.command.server-tooltip-offer-connect-server=Klicke, um dich mit diesem Server zu verbinden -velocity.command.glist-player-singular={0} Spieler ist derzeit mit dem Proxy verbunden. -velocity.command.glist-player-plural={0} Spieler sind derzeit mit dem Proxy verbunden. +velocity.command.glist-player-singular= Spieler ist derzeit mit dem Proxy verbunden. +velocity.command.glist-player-plural= Spieler sind derzeit mit dem Proxy verbunden. velocity.command.glist-view-all=Um alle Spieler auf Servern aufzulisten, verwende /glist all. velocity.command.reload-success=Velocity-Konfiguration erfolgreich neu geladen. velocity.command.reload-failure=Die Velocity-Konfiguration konnte nicht neu geladen werden. Prüfe die Konsole für weitere Details. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} ist lizenziert unter den Bedingungen der GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . ist lizenziert unter den Bedingungen der GNU General Public License v3. velocity.command.no-plugins=Es sind derzeit keine Plugins installiert. -velocity.command.plugins-list=Plugins\: {0} -velocity.command.plugin-tooltip-website=Webseite\: {0} -velocity.command.plugin-tooltip-author=Entwickler\: {0} -velocity.command.plugin-tooltip-authors=Entwickler\: {0} +velocity.command.plugins-list=Plugins\: +velocity.command.plugin-tooltip-website=Webseite\: +velocity.command.plugin-tooltip-author=Entwickler\: +velocity.command.plugin-tooltip-authors=Entwickler\: velocity.command.dump-uploading=Erfasste Daten werden hochgeladen... velocity.command.dump-send-error=Bei der Kommunikation mit den Velocity-Servern ist ein Fehler aufgetreten. Diese Server sind möglicherweise vorübergehend nicht verfügbar oder es gibt ein Problem mit deinen Netzwerkeinstellungen. Weitere Informationen findest du in der Log-Datei oder in der Konsole deines Velocity-Servers. velocity.command.dump-success=Ein anonymisierter Bericht mit nützlichen Informationen über diesen Proxy wurde erstellt. Wenn ein Entwickler den Bericht angefordert hat, kannst du diesen über folgenden Link mit ihm teilen\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_es_ES.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_es_ES.properties index 0d484bc9..00b0bea5 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_es_ES.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_es_ES.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=¡Ya estás conectado a este servidor\! velocity.error.already-connected-proxy=¡Ya estás conectado a este proxy\! velocity.error.already-connecting=¡Ya estás intentando conectarte a un servidor\! -velocity.error.cant-connect=No se ha podido conectar a {0}\: {1} -velocity.error.connecting-server-error=No hemos podido conectarte a {0}. Por favor, inténtalo de nuevo más tarde. -velocity.error.connected-server-error=Tu conexión a {0} ha sufrido un problema. +velocity.error.cant-connect=No se ha podido conectar a \: +velocity.error.connecting-server-error=No hemos podido conectarte a . Por favor, inténtalo de nuevo más tarde. +velocity.error.connected-server-error=Tu conexión a ha sufrido un problema. velocity.error.internal-server-connection-error=Se ha producido un error interno en la conexión al servidor. velocity.error.logging-in-too-fast=Estás iniciando sesión demasiado rápido, inténtalo de nuevo más tarde. velocity.error.online-mode-only=No has iniciado sesión con tu cuenta de Minecraft. Si crees que ya lo estás, intenta reiniciar tu cliente de Minecraft. velocity.error.player-connection-error=Se ha producido un error interno en tu conexión. velocity.error.modern-forwarding-needs-new-client=Este servidor solo es compatible con Minecraft 1.13 y superior. velocity.error.modern-forwarding-failed=El servidor no ha enviado una solicitud de reenvío al proxy. Asegúrate de que tu servidor está configurado para usar el método de reenvío de Velocity. -velocity.error.moved-to-new-server=Has sido echado de {0}\: {1} +velocity.error.moved-to-new-server=Has sido echado de \: velocity.error.no-available-servers=No hay servidores disponibles a los que conectarte. Inténtalo de nuevo más tarde o contacta con un administrador. velocity.error.illegal-chat-characters=Caracteres no válidos en el chat # Commands velocity.command.generic-error=Se ha producido un error al ejecutar este comando. velocity.command.command-does-not-exist=Este comando no existe. velocity.command.players-only=Solo los jugadores pueden ejecutar este comando. -velocity.command.server-does-not-exist=El servidor especificado {0} no existe. -velocity.command.player-not-found=El jugador especificado {0} no existe. -velocity.command.server-current-server=Estás conectado a {0}. +velocity.command.server-does-not-exist=El servidor especificado no existe. +velocity.command.player-not-found=El jugador especificado no existe. +velocity.command.server-current-server=Estás conectado a . velocity.command.server-too-many=Hay demasiados servidores registrados. Usa la finalización con tabulación para ver todos los servidores disponibles. velocity.command.server-available=Servidores disponibles\: -velocity.command.server-tooltip-player-online={0} jugador conectado -velocity.command.server-tooltip-players-online={0} jugadores conectados +velocity.command.server-tooltip-player-online= jugador conectado +velocity.command.server-tooltip-players-online= jugadores conectados velocity.command.server-tooltip-current-server=Estás conectado a este servidor velocity.command.server-tooltip-offer-connect-server=Haz clic para conectarte a este servidor -velocity.command.glist-player-singular={0} jugador está conectado al proxy. -velocity.command.glist-player-plural={0} jugadores están conectados al proxy. +velocity.command.glist-player-singular= jugador está conectado al proxy. +velocity.command.glist-player-plural= jugadores están conectados al proxy. velocity.command.glist-view-all=Para ver todos los jugadores por servidores, usa /glist all. velocity.command.reload-success=La configuración de Velocity ha sido recargada correctamente. velocity.command.reload-failure=No ha sido posible recargar la configuración de Velocity. Para obtener más información, revisa la consola. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} está licenciado bajo los términos de la Licencia Pública General de GNU v3. +velocity.command.version-copyright=Copyright 2018- . está licenciado bajo los términos de la Licencia Pública General de GNU v3. velocity.command.no-plugins=Actualmente no hay plugins instalados. -velocity.command.plugins-list=Complementos\: {0} -velocity.command.plugin-tooltip-website=Página web\: {0} -velocity.command.plugin-tooltip-author=Autor\: {0} -velocity.command.plugin-tooltip-authors=Autores\: {0} +velocity.command.plugins-list=Complementos\: +velocity.command.plugin-tooltip-website=Página web\: +velocity.command.plugin-tooltip-author=Autor\: +velocity.command.plugin-tooltip-authors=Autores\: velocity.command.dump-uploading=Subiendo la información recopilada... velocity.command.dump-send-error=Se ha producido un error al comunicarse con los servidores de Velocity. Es posible que los servidores no estén disponibles temporalmente o que exista un problema en tu configuración de red. Puedes encontrar más información en el archivo de registro o la consola de tu servidor Velocity. velocity.command.dump-success=Se ha creado un informe anónimo que contiene información útil sobre este proxy. Si un desarrollador lo solicita, puedes compartir el siguiente enlace con él\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_et_EE.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_et_EE.properties index 0e4ea63d..a46fa3e8 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_et_EE.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_et_EE.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Sa oled juba antud serveriga ühendatud\! velocity.error.already-connected-proxy=Sa oled juba antud proksiga ühendatud\! velocity.error.already-connecting=Sa juba ühendad severiga\! -velocity.error.cant-connect=Unable to connect to {0}\: {1} -velocity.error.connecting-server-error=Unable to connect you to {0}. Please try again later. -velocity.error.connected-server-error=Your connection to {0} encountered a problem. +velocity.error.cant-connect=Unable to connect to \: +velocity.error.connecting-server-error=Unable to connect you to . Please try again later. +velocity.error.connected-server-error=Your connection to encountered a problem. velocity.error.internal-server-connection-error=An internal server connection error occurred. velocity.error.logging-in-too-fast=Sa logid sisse liiga kiiresti, proovi hiljem uuesti. velocity.error.online-mode-only=You are not logged into your Minecraft account. If you are logged into your Minecraft account, try restarting your Minecraft client. velocity.error.player-connection-error=An internal error occurred in your connection. velocity.error.modern-forwarding-needs-new-client=This server is only compatible with Minecraft 1.13 and above. velocity.error.modern-forwarding-failed=Your server did not send a forwarding request to the proxy. Make sure the server is configured for Velocity forwarding. -velocity.error.moved-to-new-server=You were kicked from {0}\: {1} +velocity.error.moved-to-new-server=You were kicked from \: velocity.error.no-available-servers=There are no available servers to connect you to. Try again later or contact an admin. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=An error occurred while running this command. velocity.command.command-does-not-exist=Antud käsklust ei eksisteeri. velocity.command.players-only=Ainult mängijad saavad antud käsklust kasutada. -velocity.command.server-does-not-exist=Server {0} ei eksisteeri. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Sa oled hetkel ühendatud serveriga {0}. +velocity.command.server-does-not-exist=Server ei eksisteeri. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Sa oled hetkel ühendatud serveriga . velocity.command.server-too-many=There are too many servers set up. Use tab completion to view all servers available. velocity.command.server-available=Saadaolevad serverid\: -velocity.command.server-tooltip-player-online={0} mängija online -velocity.command.server-tooltip-players-online={0} mängijat online +velocity.command.server-tooltip-player-online= mängija online +velocity.command.server-tooltip-players-online= mängijat online velocity.command.server-tooltip-current-server=Currently connected to this server velocity.command.server-tooltip-offer-connect-server=Vajuta, et ühendada antud serveriga -velocity.command.glist-player-singular={0} player is currently connected to the proxy. -velocity.command.glist-player-plural={0} players are currently connected to the proxy. +velocity.command.glist-player-singular= player is currently connected to the proxy. +velocity.command.glist-player-plural= players are currently connected to the proxy. velocity.command.glist-view-all=Et näha kõiki mängijaid kõikides serverites, kasuta käsklust /glist all. velocity.command.reload-success=Velocity seadistus edukalt taaslaetud. velocity.command.reload-failure=Unable to reload your Velocity configuration. Check the console for more details. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=There are no plugins currently installed. -velocity.command.plugins-list=Pluginad\: {0} -velocity.command.plugin-tooltip-website=Veebileht\: {0} -velocity.command.plugin-tooltip-author=Autor\: {0} -velocity.command.plugin-tooltip-authors=Autorid\: {0} +velocity.command.plugins-list=Pluginad\: +velocity.command.plugin-tooltip-website=Veebileht\: +velocity.command.plugin-tooltip-author=Autor\: +velocity.command.plugin-tooltip-authors=Autorid\: velocity.command.dump-uploading=Kogutud informatsiooni üleslaadimine... velocity.command.dump-send-error=An error occurred while communicating with the Velocity servers. The servers may be temporarily unavailable or there is an issue with your network settings. You can find more information in the log or console of your Velocity server. velocity.command.dump-success=Created an anonymised report containing useful information about this proxy. If a developer requested it, you may share the following link with them\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fi_FI.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fi_FI.properties index 39c8f374..beb49a4d 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fi_FI.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fi_FI.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Olet jo yhteydessä tälle palvelimelle\! velocity.error.already-connected-proxy=Olet jo yhteydessä tälle välityspalvelimelle\! velocity.error.already-connecting=Yrität jo yhdistää palvelimeen\! -velocity.error.cant-connect={0} ei saatu yhteyttä\: {1} -velocity.error.connecting-server-error=Yhteyttä palvelimeen {0} ei voitu muodostaa. Yritä myöhemmin uudelleen. -velocity.error.connected-server-error=Yhteydessäsi palvelimeen {0} tapahtui virhe. +velocity.error.cant-connect= ei saatu yhteyttä\: +velocity.error.connecting-server-error=Yhteyttä palvelimeen ei voitu muodostaa. Yritä myöhemmin uudelleen. +velocity.error.connected-server-error=Yhteydessäsi palvelimeen tapahtui virhe. velocity.error.internal-server-connection-error=Tapahtui palvelimen sisäinen yhteysvirhe. velocity.error.logging-in-too-fast=Kirjaudut sisään liian nopeasti, yritä uudelleen hetken kuluttua. velocity.error.online-mode-only=Et ole kirjautuneena Minecraft-tilillesi. Jos olet kirjautuneena sisään, yritä pelin uudelleenkäynnistämistä. velocity.error.player-connection-error=Yhteydessäsi tapahtui sisäinen virhe. velocity.error.modern-forwarding-needs-new-client=Tämä palvelin on yhteensopiva vain Minecraft 1.13\:n ja sitä uudempien versioiden kanssa. velocity.error.modern-forwarding-failed=Valitsemasi palvelin ei lähettänyt välityspyyntöä välityspalvelimelle. Tarkista että palvelin on määritetty oikein Velocityä varten. -velocity.error.moved-to-new-server=Sinut potkittiin pois palvelimelta {0}\: {1} +velocity.error.moved-to-new-server=Sinut potkittiin pois palvelimelta \: velocity.error.no-available-servers=Yhtään palvelinta ei ole tällä hetkellä saatavilla. Yritä myöhemmin uudelleen tai ota yhteyttä palvelimen ylläpitäjään. velocity.error.illegal-chat-characters=Kiellettyjä merkkejä chatissa # Commands velocity.command.generic-error=Tämän komennon suorittamisessa tapahtui virhe. velocity.command.command-does-not-exist=Tuota komentoa ei ole olemassa. velocity.command.players-only=Vain pelaajat voivat käyttää tuota komentoa. -velocity.command.server-does-not-exist=Palvelinta {0} ei ole olemassa. -velocity.command.player-not-found=Annettua pelaajaa {0} ei ole olemassa. -velocity.command.server-current-server=Olet tällä hetkellä yhdistettynä palvelimeen {0}. +velocity.command.server-does-not-exist=Palvelinta ei ole olemassa. +velocity.command.player-not-found=Annettua pelaajaa ei ole olemassa. +velocity.command.server-current-server=Olet tällä hetkellä yhdistettynä palvelimeen . velocity.command.server-too-many=Liian monta palvelinta on määritetty. Paina Tab -näppäintä nähdäksesi kaikki saatavilla olevat palvelimet. velocity.command.server-available=Saatavilla olevat palvelimet\: -velocity.command.server-tooltip-player-online={0} pelaaja paikalla -velocity.command.server-tooltip-players-online={0} pelaajaa paikalla +velocity.command.server-tooltip-player-online= pelaaja paikalla +velocity.command.server-tooltip-players-online= pelaajaa paikalla velocity.command.server-tooltip-current-server=Tällä hetkellä yhdistetty tähän palvelimeen velocity.command.server-tooltip-offer-connect-server=Napsauta yhdistääksesi tähän palvelimeen -velocity.command.glist-player-singular={0} pelaaja on tällä hetkellä yhdistänyt välityspalvelimelle. -velocity.command.glist-player-plural={0} pelaajaa on tällä hetkellä yhdistänyt välityspalvelimelle. +velocity.command.glist-player-singular= pelaaja on tällä hetkellä yhdistänyt välityspalvelimelle. +velocity.command.glist-player-plural= pelaajaa on tällä hetkellä yhdistänyt välityspalvelimelle. velocity.command.glist-view-all=Nähdäksesi pelaajat kaikilla palvelimilla, käytä komentoa /glist all. velocity.command.reload-success=Velocityn konfiguraatio uudelleenladattiin onnistuneesti. velocity.command.reload-failure=Velocityn konfiguraation uudelleenlataus epäonnistui. Katso tarkemmat lisätiedot konsolista. -velocity.command.version-copyright=Tekijänoikeus 2018-{2} {0}. {1} on lisensoitu GNU General Public License v3\:n ehtojen mukaisesti. +velocity.command.version-copyright=Tekijänoikeus 2018- . on lisensoitu GNU General Public License v3\:n ehtojen mukaisesti. velocity.command.no-plugins=Yhtäkään pluginia ei ole asennettu. -velocity.command.plugins-list=Pluginit\: {0} -velocity.command.plugin-tooltip-website=Verkkosivu\: {0} -velocity.command.plugin-tooltip-author=Tekijä\: {0} -velocity.command.plugin-tooltip-authors=Tekijät\: {0} +velocity.command.plugins-list=Pluginit\: +velocity.command.plugin-tooltip-website=Verkkosivu\: +velocity.command.plugin-tooltip-author=Tekijä\: +velocity.command.plugin-tooltip-authors=Tekijät\: velocity.command.dump-uploading=Lähetetään kerättyjä tietoja... velocity.command.dump-send-error=Velocity-palvelimien kanssa kommunikoidessa tapahtui virhe. Palvelimet eivät ehkä ole tilapäisesti käytettävissä tai verkkoasetuksissa on ongelma. Löydät lisätietoja Velocity-palvelimesi lokista tai konsolista. velocity.command.dump-success=Luotiin anonyymi raportti, joka sisältää hyödyllistä tietoa tästä välityspalvelimesta. Jos jokin kehittäjä on pyytänyt sitä, voit jakaa seuraavan linkin heidän kanssaan\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fr_FR.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fr_FR.properties index 64fa63ee..50d2a556 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fr_FR.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fr_FR.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Vous êtes déjà connecté(e) à ce serveur \! velocity.error.already-connected-proxy=Vous êtes déjà connecté(e) à ce proxy \! velocity.error.already-connecting=Vous êtes déjà en train d'essayer de vous connecter à un serveur \! -velocity.error.cant-connect=Impossible de se connecter à {0} \: {1} -velocity.error.connecting-server-error=Impossible de vous connecter à {0}. Veuillez réessayer ultérieurement. -velocity.error.connected-server-error=Votre connexion à {0} a rencontré un problème. +velocity.error.cant-connect=Impossible de se connecter à \: +velocity.error.connecting-server-error=Impossible de vous connecter à . Veuillez réessayer ultérieurement. +velocity.error.connected-server-error=Votre connexion à a rencontré un problème. velocity.error.internal-server-connection-error=Une erreur interne s'est produite lors de la connexion au serveur. velocity.error.logging-in-too-fast=Vous vous connectez trop rapidement, réessayez ultérieurement. velocity.error.online-mode-only=Vous n'êtes pas connecté(e) à votre compte Minecraft. Si vous l'êtes, essayez de redémarrer votre client Minecraft. velocity.error.player-connection-error=Une erreur interne s'est produite lors de votre connexion. velocity.error.modern-forwarding-needs-new-client=Ce serveur est uniquement compatible avec Minecraft 1.13 et les versions ultérieures. velocity.error.modern-forwarding-failed=Votre serveur n'a pas envoyé de requête de transfert vers le proxy. Assurez-vous que le serveur est configuré pour le transfert Velocity. -velocity.error.moved-to-new-server=Vous avez été expulsé(e) de {0} \: {1} +velocity.error.moved-to-new-server=Vous avez été expulsé(e) de \: velocity.error.no-available-servers=Il n'y a pas de serveurs disponibles auxquels vous connecter. Réessayez ultérieurement ou contactez un administrateur. velocity.error.illegal-chat-characters=Caractères interdits dans le chat. # Commands velocity.command.generic-error=Une erreur est survenue lors de l'exécution de cette commande. velocity.command.command-does-not-exist=Cette commande n'existe pas. velocity.command.players-only=Seuls les joueurs peuvent exécuter cette commande. -velocity.command.server-does-not-exist=Le serveur spécifié {0} n'existe pas. -velocity.command.player-not-found=Le joueur spécifié {0} n'existe pas. -velocity.command.server-current-server=Vous êtes actuellement connecté(e) à {0}. +velocity.command.server-does-not-exist=Le serveur spécifié n'existe pas. +velocity.command.player-not-found=Le joueur spécifié n'existe pas. +velocity.command.server-current-server=Vous êtes actuellement connecté(e) à . velocity.command.server-too-many=Il y a trop de serveurs configurés. Utilisez la saisie semi-automatique via la touche Tab pour afficher tous les serveurs disponibles. velocity.command.server-available=Serveurs disponibles \: -velocity.command.server-tooltip-player-online={0} joueur connecté -velocity.command.server-tooltip-players-online={0} joueurs connectés +velocity.command.server-tooltip-player-online= joueur connecté +velocity.command.server-tooltip-players-online= joueurs connectés velocity.command.server-tooltip-current-server=Actuellement connecté(e) à ce serveur velocity.command.server-tooltip-offer-connect-server=Cliquez pour vous connecter à ce serveur -velocity.command.glist-player-singular={0} joueur est actuellement connecté au proxy. -velocity.command.glist-player-plural={0} joueurs sont actuellement connectés au proxy. +velocity.command.glist-player-singular= joueur est actuellement connecté au proxy. +velocity.command.glist-player-plural= joueurs sont actuellement connectés au proxy. velocity.command.glist-view-all=Pour afficher tous les joueurs connectés aux serveurs, utilisez /glist all. velocity.command.reload-success=Configuration de Velocity rechargée avec succès. velocity.command.reload-failure=Impossible de recharger votre configuration de Velocity. Consultez la console pour plus de détails. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} est sous la licence GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . est sous la licence GNU General Public License v3. velocity.command.no-plugins=Il n'y a aucun plugin actuellement installé. -velocity.command.plugins-list=Plugins \: {0} -velocity.command.plugin-tooltip-website=Site Internet \: {0} -velocity.command.plugin-tooltip-author=Auteur \: {0} -velocity.command.plugin-tooltip-authors=Auteurs \: {0} +velocity.command.plugins-list=Plugins \: +velocity.command.plugin-tooltip-website=Site Internet \: +velocity.command.plugin-tooltip-author=Auteur \: +velocity.command.plugin-tooltip-authors=Auteurs \: velocity.command.dump-uploading=Envoi des informations collectées... velocity.command.dump-send-error=Une erreur est survenue lors de la communication avec les serveurs Velocity. Soit les serveurs sont temporairement indisponibles, soit il y a un problème avec les paramètres de votre réseau. Vous trouverez plus d'informations dans le journal ou la console de votre serveur Velocity. velocity.command.dump-success=Un rapport anonyme contenant des informations utiles sur ce proxy a été créé. Si un développeur vous le demande, vous pouvez le partager avec le lien suivant \: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_he_IL.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_he_IL.properties index 85cd9f99..dbe34bbf 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_he_IL.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_he_IL.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=אתה כבר מחובר לשרת זה\! velocity.error.already-connected-proxy=אתה כבר מחובר ל- proxy זה\! velocity.error.already-connecting=אתה כבר מנסה להתחבר לשרת\! -velocity.error.cant-connect=לא ניתן להתחבר ל- {0}\: {1} -velocity.error.connecting-server-error=לא ניתן לחבר אותך ל- {0}. אנא נסה שנית מאוחר יותר. -velocity.error.connected-server-error=החיבור שלך ל- {0} נתקל בבעיה. +velocity.error.cant-connect=לא ניתן להתחבר ל- \: +velocity.error.connecting-server-error=לא ניתן לחבר אותך ל- . אנא נסה שנית מאוחר יותר. +velocity.error.connected-server-error=החיבור שלך ל- נתקל בבעיה. velocity.error.internal-server-connection-error=התרחשה שגיאת חיבור שרת פנימית. velocity.error.logging-in-too-fast=אתה נכנס מהר מדי, נסה שוב מאוחר יותר. velocity.error.online-mode-only=אינך מחובר לחשבון Minecraft שלך. אם אתה מחובר לחשבון Minecraft שלך, נסה להפעיל מחדש את המשחק שלך. velocity.error.player-connection-error=התרחשה שגיאה פנימית בחיבור שלך. velocity.error.modern-forwarding-needs-new-client=שרת זה תואם רק עם Minecraft 1.13 ומעלה. velocity.error.modern-forwarding-failed=השרת שלך לא שלח בקשת העברה ל- proxy. ודא שתצורת השרת נקבעה להעברת Velocity. -velocity.error.moved-to-new-server=אתה הורחקת מ- {0}\: {1} +velocity.error.moved-to-new-server=אתה הורחקת מ- \: velocity.error.no-available-servers=אין שרתים זמינים אליהם יש לחבר אותך. נסה שוב מאוחר יותר או פנה למנהל מערכת. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=התרחשה שגיאה בעת הפעלת פקודה זו. velocity.command.command-does-not-exist=פקודה זו אינה קיימת. velocity.command.players-only=רק שחקנים יכולים להפעיל פקודה זו. -velocity.command.server-does-not-exist=השרת {0} שצוין אינו קיים. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=אתה מחובר כעת ל- {0}. +velocity.command.server-does-not-exist=השרת שצוין אינו קיים. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=אתה מחובר כעת ל- . velocity.command.server-too-many=הוגדרו שרתים רבים מדי. השתמש בהשלמת כרטיסיה כדי להציג את כל השרתים הזמינים. velocity.command.server-available=שרתים זמינים\: -velocity.command.server-tooltip-player-online=שחקן {0} מחובר -velocity.command.server-tooltip-players-online={0} שחקנים מחוברים +velocity.command.server-tooltip-player-online=שחקן מחובר +velocity.command.server-tooltip-players-online= שחקנים מחוברים velocity.command.server-tooltip-current-server=מחובר כעת לשרת זה velocity.command.server-tooltip-offer-connect-server=לחץ על מנת להתחבר לשרת זה -velocity.command.glist-player-singular=שחקן {0} כעת מחובר ל- proxy. -velocity.command.glist-player-plural={0} שחקנים מחוברים כעת ל- proxy. +velocity.command.glist-player-singular=שחקן כעת מחובר ל- proxy. +velocity.command.glist-player-plural= שחקנים מחוברים כעת ל- proxy. velocity.command.glist-view-all=כדי להציג את כל השחקנים בשרתים, השתמש ב- glist all/. velocity.command.reload-success=תצורת Velocity נטענה מחדש בהצלחה. velocity.command.reload-failure=אין אפשרות לטעון מחדש את תצורת ה- Velocity שלך. עיין בקונסולה לקבלת פרטים נוספים. -velocity.command.version-copyright=זכויות יוצרים 2018-{2} {0}. {1} מורשה על פי תנאי v3 הרישיון הציבורי הכללי של GNU. +velocity.command.version-copyright=זכויות יוצרים 2018- . מורשה על פי תנאי v3 הרישיון הציבורי הכללי של GNU. velocity.command.no-plugins=לא מותקנים כעת תוספים. -velocity.command.plugins-list=תוספים\: {0} -velocity.command.plugin-tooltip-website=אתר אינטרנט\: {0} -velocity.command.plugin-tooltip-author=יוצר\: {0} -velocity.command.plugin-tooltip-authors=יוצרים\: {0} +velocity.command.plugins-list=תוספים\: +velocity.command.plugin-tooltip-website=אתר אינטרנט\: +velocity.command.plugin-tooltip-author=יוצר\: +velocity.command.plugin-tooltip-authors=יוצרים\: velocity.command.dump-uploading=מעלה מידע שנאסף... velocity.command.dump-send-error=התרחשה שגיאה בעת קיום תקשורת עם שרתי ה- Velocity. ייתכן שהשרתים אינם זמינים באופן זמני או שקיימת בעיה בהגדרות הרשת שלך. באפשרותך למצוא מידע נוסף ביומן הרישום או בקונסולה של שרת ה- Velocity שלך. velocity.command.dump-success=נוצר דוח אנונימיות המכיל מידע שימושי אודות proxy זה. אם מפתח ביקש זאת, באפשרותך לשתף איתם את הקישור הבא\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_hu_HU.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_hu_HU.properties index b43f69b5..80861b04 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_hu_HU.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_hu_HU.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Már csatlakozva vagy ehhez a szerverhez\! velocity.error.already-connected-proxy=Már csatlakozva vagy ehhez a proxyhoz\! velocity.error.already-connecting=Jelenleg is csatlakozol egy szerverre\! -velocity.error.cant-connect=Nem lehet csatlakozni a(z) {0} szerverhez\: {1} -velocity.error.connecting-server-error=Nem tudunk csatlakoztatni a(z) {0} szerverhez. Kérlek próbáld újra később. -velocity.error.connected-server-error=A kapcsolatod a(z) {0} szerverhez hibába ütközött. +velocity.error.cant-connect=Nem lehet csatlakozni a(z) szerverhez\: +velocity.error.connecting-server-error=Nem tudunk csatlakoztatni a(z) szerverhez. Kérlek próbáld újra később. +velocity.error.connected-server-error=A kapcsolatod a(z) szerverhez hibába ütközött. velocity.error.internal-server-connection-error=Egy szerveroldali hiba történt. velocity.error.logging-in-too-fast=Túl gyorsan próbálsz csatlakozni, próbáld újra később. velocity.error.online-mode-only=Nem vagy belépve a Minecraft profilodba. Ha mégis be vagy lépve, kérlek próbáld újraindítani a Minecraft kliensedet. velocity.error.player-connection-error=Egy belső hiba keletkezett a kapcsolatodban. velocity.error.modern-forwarding-needs-new-client=Ez a szerver csak a Minecraft 1.13 és afölötti verziókkal kompatibilis. velocity.error.modern-forwarding-failed=A szerver ahonnan csatlakoztál nem küldött modern átirányítási kérelmet a proxy felé. Bizonyosodj meg róla, hogy a szerver be van állítva a modern Velocity átirányítás használatára. -velocity.error.moved-to-new-server=Ki lettél rúgva a(z) {0} szerverről\: {1} +velocity.error.moved-to-new-server=Ki lettél rúgva a(z) szerverről\: velocity.error.no-available-servers=Jelenleg nincs elérhető szerver ahová csatlakoztatni tudnánk. Próbáld újra később, vagy lépj kapcsolatba egy adminisztrátorral. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=Hiba történt a parancs futtatása közben. velocity.command.command-does-not-exist=Ilyen parancs nem létezik. velocity.command.players-only=Ezt a parancsot csak játékosok használhatják. -velocity.command.server-does-not-exist=A megadott szerver ({0}) nem létezik. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Jelenleg a(z) {0} szerveren tartózkodsz. +velocity.command.server-does-not-exist=A megadott szerver () nem létezik. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Jelenleg a(z) szerveren tartózkodsz. velocity.command.server-too-many=Túl sok szerver van beállítva. Használd a tab parancs befejező funkciót, hogy megnézd az összes elérhető szervert. velocity.command.server-available=Elérhető szerverek\: -velocity.command.server-tooltip-player-online={0} játékos online -velocity.command.server-tooltip-players-online={0} játékos online +velocity.command.server-tooltip-player-online= játékos online +velocity.command.server-tooltip-players-online= játékos online velocity.command.server-tooltip-current-server=Jelenleg ezen a szerveren tartózkodsz velocity.command.server-tooltip-offer-connect-server=Kattints ide, hogy csatlakozz erre a szerverre -velocity.command.glist-player-singular={0} játékos van jelenleg csatlakozva a proxyhoz. -velocity.command.glist-player-plural={0} játékos van jelenleg csatlakozva a proxyhoz. +velocity.command.glist-player-singular= játékos van jelenleg csatlakozva a proxyhoz. +velocity.command.glist-player-plural= játékos van jelenleg csatlakozva a proxyhoz. velocity.command.glist-view-all=Hogy megnézd az összes játékost az összes szerveren, használd a /glist all parancsot. velocity.command.reload-success=A Velocity beállításai sikeresen frissítve lettek. velocity.command.reload-failure=Hiba történt a Velocity beállításainak frissítése közben. Több információt a konzolban találsz. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} licenszelve van a GNU General Public License v3 alatt. +velocity.command.version-copyright=Copyright 2018- . licenszelve van a GNU General Public License v3 alatt. velocity.command.no-plugins=Jelenleg egyetlen plugin sincs telepítve. -velocity.command.plugins-list=Pluginok\: {0} -velocity.command.plugin-tooltip-website=Weboldal\: {0} -velocity.command.plugin-tooltip-author=Készítő\: {0} -velocity.command.plugin-tooltip-authors=Készítők\: {0} +velocity.command.plugins-list=Pluginok\: +velocity.command.plugin-tooltip-website=Weboldal\: +velocity.command.plugin-tooltip-author=Készítő\: +velocity.command.plugin-tooltip-authors=Készítők\: velocity.command.dump-uploading=Az összegyűjtött információ feltöltése... velocity.command.dump-send-error=Egy hiba lépett fel miközben kommunikálni próbáltunk a Velocity szerverekkel. Lehetséges, hogy a szerver(ek) ideiglenesen elérhetetlenek, vagy ez egy hiba a te hálózati beállításaiddal. Több információt a logban, vagy a Velocity konzoljában találsz. velocity.command.dump-success=Egy anonimizált jelentés sikeresen el lett készítve erről a proxyról. Ha egy fejlesztő kérte, akkor innen kimásolhatod a linket, és megoszthatod velük\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_it_IT.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_it_IT.properties index d2a79886..93ffc11f 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_it_IT.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_it_IT.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Sei già connesso a questo server\! velocity.error.already-connected-proxy=Sei già connesso a questo proxy\! velocity.error.already-connecting=Stai già cercando di connetterti a un server\! -velocity.error.cant-connect=Impossibile connettersi a {0}\: {1} -velocity.error.connecting-server-error=Impossibile connetterti a {0}. Prova più tardi. -velocity.error.connected-server-error=La tua connessione a {0} ha incontrato un problema. +velocity.error.cant-connect=Impossibile connettersi a \: +velocity.error.connecting-server-error=Impossibile connetterti a . Prova più tardi. +velocity.error.connected-server-error=La tua connessione a ha incontrato un problema. velocity.error.internal-server-connection-error=Si è verificato un errore di connessione interna al server. velocity.error.logging-in-too-fast=Stai accedendo troppo velocemente, riprova più tardi. velocity.error.online-mode-only=Non sei connesso al tuo account Minecraft. Se hai effettuato l'accesso al tuo account Minecraft, prova a riavviare il tuo client Minecraft. velocity.error.player-connection-error=Si è verificato un errore interno nella connessione. velocity.error.modern-forwarding-needs-new-client=Questo server è compatibile solo con versioni uguali o superiori a Minecraft 1.13. velocity.error.modern-forwarding-failed=Il server non ha inviato una richiesta di inoltro al proxy. Assicurati che il server sia configurato per l'inoltro di Velocity. -velocity.error.moved-to-new-server=Sei stato cacciato da {0}\: {1} +velocity.error.moved-to-new-server=Sei stato cacciato da \: velocity.error.no-available-servers=Non ci sono server disponibili per connetterti. Riprova più tardi o contatta un amministratore. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=Si è verificato un errore durante l'esecuzione di questo comando. velocity.command.command-does-not-exist=Questo comando non esiste. velocity.command.players-only=Solo i giocatori possono eseguire questo comando. -velocity.command.server-does-not-exist=Il server {0} non esiste. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Sei già connesso a {0}. +velocity.command.server-does-not-exist=Il server non esiste. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Sei già connesso a . velocity.command.server-too-many=Ci sono troppi server impostati. Usa il completamento con il tasto tab per visualizzare tutti i server disponibili. velocity.command.server-available=Server disponibili\: -velocity.command.server-tooltip-player-online={0} giocatore online -velocity.command.server-tooltip-players-online={0} giocatori online +velocity.command.server-tooltip-player-online= giocatore online +velocity.command.server-tooltip-players-online= giocatori online velocity.command.server-tooltip-current-server=Sei già connesso a questo server velocity.command.server-tooltip-offer-connect-server=Clicca per connetterti a questo server -velocity.command.glist-player-singular={0} giocatore è attualmente connesso al proxy. -velocity.command.glist-player-plural={0} giocatori sono attualmente connessi al proxy. +velocity.command.glist-player-singular= giocatore è attualmente connesso al proxy. +velocity.command.glist-player-plural= giocatori sono attualmente connessi al proxy. velocity.command.glist-view-all=Per visualizzare tutti i giocatori sui server, usa /glist all. velocity.command.reload-success=La configurazione di Velocity è stata ricaricata con successo. velocity.command.reload-failure=Impossibile ricaricare la configurazione di Velocity. Controlla la console per maggiori dettagli. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} è concesso in licenza secondo i termini della GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . è concesso in licenza secondo i termini della GNU General Public License v3. velocity.command.no-plugins=Non ci sono plugin installati. -velocity.command.plugins-list=Plugins\: {0} -velocity.command.plugin-tooltip-website=Sito web\: {0} -velocity.command.plugin-tooltip-author=Autore\: {0} -velocity.command.plugin-tooltip-authors=Autori\: {0} +velocity.command.plugins-list=Plugins\: +velocity.command.plugin-tooltip-website=Sito web\: +velocity.command.plugin-tooltip-author=Autore\: +velocity.command.plugin-tooltip-authors=Autori\: velocity.command.dump-uploading=Caricamento informazioni raccolte... velocity.command.dump-send-error=Si è verificato un errore durante la comunicazione con i server Velocity. I server potrebbero essere temporaneamente non disponibili o c'è un problema con le impostazioni di rete. Puoi trovare maggiori informazioni nel log o nella console del tuo server Velocity. velocity.command.dump-success=Creato un report anonimo contenente informazioni utili su questo proxy. Se uno sviluppatore lo richiede, è possibile condividere con loro il seguente link\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ja_JP.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ja_JP.properties index 235de499..3a9ba332 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ja_JP.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ja_JP.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=すでにこのサーバーに接続されています。 velocity.error.already-connected-proxy=すでにこのプロキシに接続されています。 velocity.error.already-connecting=すでにサーバーに接続しようとしています! -velocity.error.cant-connect={0} に接続できません\: {1} -velocity.error.connecting-server-error={0} に接続できませんでした。後でもう一度お試しください。 -velocity.error.connected-server-error={0} との接続に問題が発生しました。 +velocity.error.cant-connect= に接続できません\: +velocity.error.connecting-server-error= に接続できませんでした。後でもう一度お試しください。 +velocity.error.connected-server-error= との接続に問題が発生しました。 velocity.error.internal-server-connection-error=内部サーバー接続エラーが発生しました。 velocity.error.logging-in-too-fast=ログイン速度が速すぎます。後でもう一度お試しください。 velocity.error.online-mode-only=無効なセッションです(ゲームとランチャーを再起動してください) velocity.error.player-connection-error=接続中に内部エラーが発生しました。 velocity.error.modern-forwarding-needs-new-client=このサーバーは Minecraft 1.13以降のみ互換性があります。 velocity.error.modern-forwarding-failed=サーバーがプロキシに転送要求を送信しませんでした。 サーバーが Velocity 用に構成されていることを確認してください。 -velocity.error.moved-to-new-server=あなたは {0} からキックされました\: {1} +velocity.error.moved-to-new-server=あなたは からキックされました\: velocity.error.no-available-servers=接続できるサーバーがありません。後でもう一度試すか、管理者にお問い合わせください。 velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=このコマンドの実行中にエラーが発生しました。 velocity.command.command-does-not-exist=未知のコマンドです。 velocity.command.players-only=このコマンドはプレイヤーのみ実行できます。 -velocity.command.server-does-not-exist=指定されたサーバー {0} は存在しません。 -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=現在 {0} に接続しています。 +velocity.command.server-does-not-exist=指定されたサーバー は存在しません。 +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=現在 に接続しています。 velocity.command.server-too-many=設定されているサーバー数が多すぎます。タブ補完を使い、利用可能なすべてのサーバーを表示してください。 velocity.command.server-available=利用可能なサーバー\: -velocity.command.server-tooltip-player-online={0} 人のプレイヤーがオンライン -velocity.command.server-tooltip-players-online={0} 人のプレイヤーがオンライン +velocity.command.server-tooltip-player-online= 人のプレイヤーがオンライン +velocity.command.server-tooltip-players-online= 人のプレイヤーがオンライン velocity.command.server-tooltip-current-server=現在、このサーバーに接続しています velocity.command.server-tooltip-offer-connect-server=クリックしてこのサーバーに接続 -velocity.command.glist-player-singular={0} 人が現在プロキシに接続しています。 -velocity.command.glist-player-plural={0} 人が現在プロキシに接続しています。 +velocity.command.glist-player-singular= 人が現在プロキシに接続しています。 +velocity.command.glist-player-plural= 人が現在プロキシに接続しています。 velocity.command.glist-view-all=サーバー上のすべてのプレイヤーを表示するには、/glist allを使用してください。 velocity.command.reload-success=Velocityの設定が再読み込みされました。 velocity.command.reload-failure=Velocityの設定を再読み込みできません。詳細はコンソールで確認してください。 -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} は、GNU General Public License v3に基づいてライセンスされています。 +velocity.command.version-copyright=Copyright 2018- . は、GNU General Public License v3に基づいてライセンスされています。 velocity.command.no-plugins=現在インストールされているプラグインはありません。 -velocity.command.plugins-list=プラグイン\: {0} -velocity.command.plugin-tooltip-website=ウェブサイト\: {0} -velocity.command.plugin-tooltip-author=作者\: {0} -velocity.command.plugin-tooltip-authors=作者\: {0} +velocity.command.plugins-list=プラグイン\: +velocity.command.plugin-tooltip-website=ウェブサイト\: +velocity.command.plugin-tooltip-author=作者\: +velocity.command.plugin-tooltip-authors=作者\: velocity.command.dump-uploading=収集した情報をアップロードしています... velocity.command.dump-send-error=Velocityサーバーとの通信中にエラーが発生しました。サーバーが一時的に利用できないか、ネットワーク設定に問題がある可能性があります。詳細はコンソールまたはサーバーログで確認できます。 velocity.command.dump-success=このプロキシに関する有用な情報を含む匿名化されたレポートを作成しました。開発者が要求した場合は、次のリンクを共有してください\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ko_KR.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ko_KR.properties index e7e528cc..624c481f 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ko_KR.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ko_KR.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=이미 이 서버에 연결되어 있습니다\! velocity.error.already-connected-proxy=이미 이 프록시에 연결되어 있습니다\! velocity.error.already-connecting=이미 이 서버에 연결하는 중입니다\! -velocity.error.cant-connect={0}에 연결할 수 없습니다\: {1} -velocity.error.connecting-server-error={0}에 연결할 수 없습니다. 나중에 다시 시도하세요. -velocity.error.connected-server-error={0}에 연결하던 도중 문제가 발생했습니다. +velocity.error.cant-connect=에 연결할 수 없습니다\: +velocity.error.connecting-server-error=에 연결할 수 없습니다. 나중에 다시 시도하세요. +velocity.error.connected-server-error=에 연결하던 도중 문제가 발생했습니다. velocity.error.internal-server-connection-error=내부 서버 연결 오류가 발생했습니다. velocity.error.logging-in-too-fast=너무 빠르게 로그인을 시도했습니다. 나중에 다시 시도하세요. velocity.error.online-mode-only=Minecraft 계정에 로그인하지 않았습니다. Minecraft 계정에 로그인한 경우, Minecraft 클라이언트를 다시 시작해보세요. velocity.error.player-connection-error=연결에 내부 오류가 발생했습니다. velocity.error.modern-forwarding-needs-new-client=이 서버는 Minecraft 1.13 이상만 지원합니다. velocity.error.modern-forwarding-failed=서버가 프록시에 포워딩 요청을 보내지 않았습니다. 서버가 Velocity 포워딩을 사용하도록 설정되어 있는지 확인하세요. -velocity.error.moved-to-new-server={0}에서 추방됐습니다\: {1} +velocity.error.moved-to-new-server=에서 추방됐습니다\: velocity.error.no-available-servers=연결할 수 있는 서버가 없습니다. 나중에 다시 시도하거나 관리자에게 문의하세요. velocity.error.illegal-chat-characters=채팅에 올바르지 않은 문자가 있습니다. # Commands velocity.command.generic-error=명령어를 실행하는 도중 오류가 발생했습니다. velocity.command.command-does-not-exist=존재하지 않는 명령어입니다. velocity.command.players-only=이 명령어는 플레이어만 사용할 수 있습니다. -velocity.command.server-does-not-exist=지정한 서버 {0}이(가) 존재하지 않습니다. -velocity.command.player-not-found=지정한 플레이어 {0}이(가) 존재하지 않습니다. -velocity.command.server-current-server=현재 {0}에 연결되어 있습니다. +velocity.command.server-does-not-exist=지정한 서버 이(가) 존재하지 않습니다. +velocity.command.player-not-found=지정한 플레이어 이(가) 존재하지 않습니다. +velocity.command.server-current-server=현재 에 연결되어 있습니다. velocity.command.server-too-many=너무 많은 서버가 존재합니다. tab 자동완성으로 사용 가능한 모든 서버를 볼 수 있습니다. velocity.command.server-available=사용 가능한 서버\: -velocity.command.server-tooltip-player-online=플레이어 {0}명 접속 중 -velocity.command.server-tooltip-players-online=플레이어 {0}명 접속 중 +velocity.command.server-tooltip-player-online=플레이어 명 접속 중 +velocity.command.server-tooltip-players-online=플레이어 명 접속 중 velocity.command.server-tooltip-current-server=현재 이 서버에 연결되어 있습니다 velocity.command.server-tooltip-offer-connect-server=서버에 연결하려면 클릭 -velocity.command.glist-player-singular=플레이어 {0}명이 현재 프록시에 연결되어 있습니다. -velocity.command.glist-player-plural=플레이어 {0}명이 현재 프록시에 연결되어 있습니다. +velocity.command.glist-player-singular=플레이어 명이 현재 프록시에 연결되어 있습니다. +velocity.command.glist-player-plural=플레이어 명이 현재 프록시에 연결되어 있습니다. velocity.command.glist-view-all=서버에 있는 모든 플레이어를 보려면, /glist all을 사용하세요. velocity.command.reload-success=Velocity 설정을 성공적으로 다시 불러왔습니다. velocity.command.reload-failure=Velocity 설정을 다시 불러올 수 없습니다. 자세한 내용은 콘솔을 확인하세요. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1}은(는) GNU General Public License v3 라이센스의 약관을 따릅니다. +velocity.command.version-copyright=Copyright 2018- . 은(는) GNU General Public License v3 라이센스의 약관을 따릅니다. velocity.command.no-plugins=설치된 플러그인이 없습니다. -velocity.command.plugins-list=플러그인\: {0} -velocity.command.plugin-tooltip-website=웹사이트\: {0} -velocity.command.plugin-tooltip-author=제작자\: {0} -velocity.command.plugin-tooltip-authors=제작자\: {0} +velocity.command.plugins-list=플러그인\: +velocity.command.plugin-tooltip-website=웹사이트\: +velocity.command.plugin-tooltip-author=제작자\: +velocity.command.plugin-tooltip-authors=제작자\: velocity.command.dump-uploading=수집된 정보를 업로드하는 중... velocity.command.dump-send-error=Velocity 서버와 통신하는 동안 오류가 발생했습니다. 서버를 일시적으로 사용할 수 없거나 네트워크 설정에 문제가 있을 수 있습니다. Velocity 서버의 로그 또는 콘솔에서 자세한 정보를 찾아볼 수 있습니다. velocity.command.dump-success=이 프록시와 관련된 유용한 정보를 담은 익명 보고서를 만들었습니다. 만약 개발자가 요청한다면, 다음 링크를 그들에게 공유해보세요\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nb_NO.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nb_NO.properties index 05a17545..d43db965 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nb_NO.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nb_NO.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Du er allerede tilkoblet denne serveren\! velocity.error.already-connected-proxy=Du er allerede tilkoblet denne proxyen\! velocity.error.already-connecting=Du tilkobles allerede en server\! -velocity.error.cant-connect=Kan ikke koble til {0}\: {1} -velocity.error.connecting-server-error=Kan ikke koble deg til {0}. Vennligst prøv igjen senere. -velocity.error.connected-server-error=Din oppkobling mot {0} traff et uventet problem. +velocity.error.cant-connect=Kan ikke koble til \: +velocity.error.connecting-server-error=Kan ikke koble deg til . Vennligst prøv igjen senere. +velocity.error.connected-server-error=Din oppkobling mot traff et uventet problem. velocity.error.internal-server-connection-error=Et internt problem oppstod. velocity.error.logging-in-too-fast=Du logger inn for raskt, prøv igjen senere. velocity.error.online-mode-only=Du er ikke innlogget på din Minecraft konto. Om du er innlogget, prøv å restarte din Minecraft klient. velocity.error.player-connection-error=Et internt problem oppstod. velocity.error.modern-forwarding-needs-new-client=Denne serveren er bare kompatibel med Minecraft 1.13 og nyere. velocity.error.modern-forwarding-failed=Din server sendte ikke en viderekoblingsforespørsel til proxyen. Pass på at serveren er konfigurert for Velocity viderekobling. -velocity.error.moved-to-new-server=Du ble sparket ut fra {0}\: {1} +velocity.error.moved-to-new-server=Du ble sparket ut fra \: velocity.error.no-available-servers=Det finnes ingen tilgjengelige servere å koble deg til. Prøv igjen senere eller kontakt en administrator. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=En feil oppstod under gjennomføringen av denne kommandoen. velocity.command.command-does-not-exist=Denne kommandoen finnes ikke. velocity.command.players-only=Bare spillere kan utføre denne kommandoen. -velocity.command.server-does-not-exist=Den spesifiserte serveren {0} finnes ikke. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Du er for øyeblikket tilkoblet {0}. +velocity.command.server-does-not-exist=Den spesifiserte serveren finnes ikke. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Du er for øyeblikket tilkoblet . velocity.command.server-too-many=Det er for mange servere satt opp. Bruk tabfullførelse for å se alle tilgjengelige servere. velocity.command.server-available=Tilgjengelige servere\: -velocity.command.server-tooltip-player-online={0} spiller tilkoblet -velocity.command.server-tooltip-players-online={0} spillere tilkoblet +velocity.command.server-tooltip-player-online= spiller tilkoblet +velocity.command.server-tooltip-players-online= spillere tilkoblet velocity.command.server-tooltip-current-server=Du er for øyeblikket tilkoblet denne serveren velocity.command.server-tooltip-offer-connect-server=Trykk for å koble til denne serveren -velocity.command.glist-player-singular={0} spiller er for øyeblikket tilkoblet denne proxyen. -velocity.command.glist-player-plural={0} spillere er for øyeblikket tilkoblet denne proxyen. +velocity.command.glist-player-singular= spiller er for øyeblikket tilkoblet denne proxyen. +velocity.command.glist-player-plural= spillere er for øyeblikket tilkoblet denne proxyen. velocity.command.glist-view-all=For å se alle tilkoblede spillere, utfør /glist all. velocity.command.reload-success=Velocity konfigurasjonen ble lastet inn på nytt. velocity.command.reload-failure=Kan ikke laste inn Velocity konfigurasjonen din. Sjekk konsollen for mer detaljer. -velocity.command.version-copyright=Opphavsrett 2018-{2} {0}. {1} er lisensiert under betingelsene av GNU General Public License v3. +velocity.command.version-copyright=Opphavsrett 2018- . er lisensiert under betingelsene av GNU General Public License v3. velocity.command.no-plugins=Ingen plugin er installert. -velocity.command.plugins-list=Plugins\: {0} -velocity.command.plugin-tooltip-website=Hjemmeside\: {0} -velocity.command.plugin-tooltip-author=Skaper\: {0} -velocity.command.plugin-tooltip-authors=Skapere\: {0} +velocity.command.plugins-list=Plugins\: +velocity.command.plugin-tooltip-website=Hjemmeside\: +velocity.command.plugin-tooltip-author=Skaper\: +velocity.command.plugin-tooltip-authors=Skapere\: velocity.command.dump-uploading=Laster opp diagnostisk informasjon... velocity.command.dump-send-error=En feil oppstod under kommunikasjon med Velocityserverne. Enten er disse serverne midlertidig utilgjengelige, ellers er det noe galt med dine nettverksinnstillinger. Du kan finne mer informasjon i din logg eller i konsollen på Velocityservern. velocity.command.dump-success=Skapte en anonymisert rapport med utfyllende informasjon om denne proxyen. Om en utvikler forespurte den, kan du dele følgende lenke med dem\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nl_NL.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nl_NL.properties index cbb5f6b8..8b93adb8 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nl_NL.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nl_NL.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Je bent al met deze server verbonden\! velocity.error.already-connected-proxy=Je bent al met deze proxy verbonden\! velocity.error.already-connecting=Je probeert al verbinding te maken met een server\! -velocity.error.cant-connect=Kan geen verbinding maken met {0}\: {1} -velocity.error.connecting-server-error=Kan je niet verbinden met {0}. Probeer het later opnieuw. -velocity.error.connected-server-error=Er is een probleem opgetreden bij je verbinding met {0}. +velocity.error.cant-connect=Kan geen verbinding maken met \: +velocity.error.connecting-server-error=Kan je niet verbinden met . Probeer het later opnieuw. +velocity.error.connected-server-error=Er is een probleem opgetreden bij je verbinding met . velocity.error.internal-server-connection-error=Er is een interne serververbindingsfout opgetreden. velocity.error.logging-in-too-fast=Je logt te snel in, probeer het later opnieuw. velocity.error.online-mode-only=Je bent niet aangemeld bij je Minecraft-account. Als je wel bent ingelogd bij je Minecraft account, probeer dan je Minecraft-client opnieuw op te starten. velocity.error.player-connection-error=Er is een interne fout opgetreden bij je verbinding. velocity.error.modern-forwarding-needs-new-client=Deze server ondersteunt alleen Minecraft 1.13 en nieuwer. velocity.error.modern-forwarding-failed=Je server heeft geen forwarding verzoek naar de proxy gestuurd. Zorg ervoor dat de server is geconfigureerd voor Velocity forwarding. -velocity.error.moved-to-new-server=Je bent verwijderd van {0}\: {1} +velocity.error.moved-to-new-server=Je bent verwijderd van \: velocity.error.no-available-servers=Er zijn geen beschikbare servers om je met te verbinden. Probeer het later opnieuw of neem contact op met een administrator. velocity.error.illegal-chat-characters=Ongeldige karakters in de chat # Commands velocity.command.generic-error=Er is een fout opgetreden bij het uitvoeren van dit commando. velocity.command.command-does-not-exist=Dit commando bestaat niet. velocity.command.players-only=Alleen spelers kunnen dit commando uitvoeren. -velocity.command.server-does-not-exist=De opgegeven server {0} bestaat niet. -velocity.command.player-not-found=De opgegeven speler {0} bestaat niet. -velocity.command.server-current-server=Je bent op dit moment verbonden met {0}. +velocity.command.server-does-not-exist=De opgegeven server bestaat niet. +velocity.command.player-not-found=De opgegeven speler bestaat niet. +velocity.command.server-current-server=Je bent op dit moment verbonden met . velocity.command.server-too-many=Er zijn te veel servers ingesteld. Gebruik tab-aanvulling om alle beschikbare servers te bekijken. velocity.command.server-available=Beschikbare servers\: -velocity.command.server-tooltip-player-online={0} speler online -velocity.command.server-tooltip-players-online={0} spelers online +velocity.command.server-tooltip-player-online= speler online +velocity.command.server-tooltip-players-online= spelers online velocity.command.server-tooltip-current-server=Momenteel verbonden met deze server velocity.command.server-tooltip-offer-connect-server=Klik om te verbinden met deze server -velocity.command.glist-player-singular={0} speler is momenteel verbonden met de proxy. -velocity.command.glist-player-plural={0} spelers zijn momenteel verbonden met de proxy. +velocity.command.glist-player-singular= speler is momenteel verbonden met de proxy. +velocity.command.glist-player-plural= spelers zijn momenteel verbonden met de proxy. velocity.command.glist-view-all=Om alle spelers op de servers te bekijken, gebruik /glist all. velocity.command.reload-success=Velocity configuratie succesvol herladen. velocity.command.reload-failure=De Velocity-configuratie kan niet opnieuw worden geladen. Kijk in de console voor meer details. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is gelicentieerd onder de voorwaarden van de GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . is gelicentieerd onder de voorwaarden van de GNU General Public License v3. velocity.command.no-plugins=Er zijn momenteel geen plugins geïnstalleerd. -velocity.command.plugins-list=Plugins\: {0} -velocity.command.plugin-tooltip-website=Website\: {0} -velocity.command.plugin-tooltip-author=Auteur\: {0} -velocity.command.plugin-tooltip-authors=Auteurs\: {0} +velocity.command.plugins-list=Plugins\: +velocity.command.plugin-tooltip-website=Website\: +velocity.command.plugin-tooltip-author=Auteur\: +velocity.command.plugin-tooltip-authors=Auteurs\: velocity.command.dump-uploading=Verzamelde informatie uploaden... velocity.command.dump-send-error=Er is een fout opgetreden tijdens de communicatie met de Velocity-servers. De servers zijn mogelijk tijdelijk niet beschikbaar of er is een probleem met je netwerkinstellingen. Je kunt meer informatie vinden in de logs of de console van je Velocity-server. velocity.command.dump-success=Een anoniem rapport is gemaakt met nuttige informatie over deze proxy. Als een ontwikkelaar dit heeft verzocht, kun je de volgende link delen\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nn_NO.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nn_NO.properties index b23ce960..8755b2ad 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nn_NO.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nn_NO.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Du er allereie tilkopla denne sørvaren\! velocity.error.already-connected-proxy=Du er allereie tilkopla denne proxyen\! velocity.error.already-connecting=Du tilkoplas allereie ein sørvar\! -velocity.error.cant-connect=Kan ikkje kopla til {0}\: {1} -velocity.error.connecting-server-error=Kan ikkje kopla deg til {0}. Prøv gjerne igjen seinare. -velocity.error.connected-server-error=Din oppkopling mot {0} trefte eit uventa problem. +velocity.error.cant-connect=Kan ikkje kopla til \: +velocity.error.connecting-server-error=Kan ikkje kopla deg til . Prøv gjerne igjen seinare. +velocity.error.connected-server-error=Din oppkopling mot trefte eit uventa problem. velocity.error.internal-server-connection-error=Eit internt problem oppstod. velocity.error.logging-in-too-fast=Du loggar inn for raskt, prøv igjen seinare. velocity.error.online-mode-only=Du er ikkje innlogga på din Minecraft konto. Dersom du er innlogga, prøv å restarta din Minecraft klient. velocity.error.player-connection-error=Eit internt problem oppstod. velocity.error.modern-forwarding-needs-new-client=Denne sørvaren er bare kompatibel med Minecraft 1.13 og nyare. velocity.error.modern-forwarding-failed=Din server sende ikkje ein vidarekoplingsførespurnad til proxyen. Pass på at sørvaren er konfigurert for Velocity vidarekopling. -velocity.error.moved-to-new-server=Du blei sparka ut frå {0}\: {1} +velocity.error.moved-to-new-server=Du blei sparka ut frå \: velocity.error.no-available-servers=Det finst ingen tilgjengelege sørvarar å kopla deg til. Prøv igjen seinare eller kontakt ein administrator. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=Ein feil oppstod under utføringa av denne kommandoen. velocity.command.command-does-not-exist=Denne kommandoen finst ikkje. velocity.command.players-only=Bare spelarar kan utføra denne kommandoen. -velocity.command.server-does-not-exist=Den oppgjevne sørvaren {0} finst ikkje. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Du er for augneblinken tilkopla {0}. +velocity.command.server-does-not-exist=Den oppgjevne sørvaren finst ikkje. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Du er for augneblinken tilkopla . velocity.command.server-too-many=Det er for mange sørvarar sette opp. Bruk tabfullføring for å sjå alle tilgjengelege sørvarar. velocity.command.server-available=Tilgjengelege sørvarar\: -velocity.command.server-tooltip-player-online={0} spelar tilkopla -velocity.command.server-tooltip-players-online={0} spelarar tilkopla +velocity.command.server-tooltip-player-online= spelar tilkopla +velocity.command.server-tooltip-players-online= spelarar tilkopla velocity.command.server-tooltip-current-server=Du er for augneblinken tilkopla denne sørvaren velocity.command.server-tooltip-offer-connect-server=Trykk for å kopla til denne sørvaren -velocity.command.glist-player-singular={0} spelar er for augneblinken tilkopla denne proxyen. -velocity.command.glist-player-plural={0} spelare er for augneblinken tilkopla denne proxyen. +velocity.command.glist-player-singular= spelar er for augneblinken tilkopla denne proxyen. +velocity.command.glist-player-plural= spelare er for augneblinken tilkopla denne proxyen. velocity.command.glist-view-all=For å sjå alle tilkopla spelarar, gjer /glist all. velocity.command.reload-success=Velocity konfigurasjonen blei lasta inn på nytt. velocity.command.reload-failure=Kan ikkje lasta inn Velocity konfigurasjonen din. Sjekk konsollen for meir detaljar. -velocity.command.version-copyright=Opphavsrett 2018-{2} {0}. {1} er lisensiert under vilkåra av GNU General Public License v3. +velocity.command.version-copyright=Opphavsrett 2018- . er lisensiert under vilkåra av GNU General Public License v3. velocity.command.no-plugins=Det er ingen plugins installert. -velocity.command.plugins-list=Plugins\: {0} -velocity.command.plugin-tooltip-website=Heimeside\: {0} -velocity.command.plugin-tooltip-author=Skapar\: {0} -velocity.command.plugin-tooltip-authors=Skaparar\: {0} +velocity.command.plugins-list=Plugins\: +velocity.command.plugin-tooltip-website=Heimeside\: +velocity.command.plugin-tooltip-author=Skapar\: +velocity.command.plugin-tooltip-authors=Skaparar\: velocity.command.dump-uploading=Uploading gathered information... velocity.command.dump-send-error=An error occurred while communicating with the Velocity servers. The servers may be temporarily unavailable or there is an issue with your network settings. You can find more information in the log or console of your Velocity server. velocity.command.dump-success=Created an anonymised report containing useful information about this proxy. If a developer requested it, you may share the following link with them\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pl_PL.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pl_PL.properties index 02a90f87..9e6764bb 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pl_PL.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pl_PL.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Jesteś już połączony z tym serwerem\! velocity.error.already-connected-proxy=Jesteś już połączony z tym proxy\! velocity.error.already-connecting=Jesteś już w trakcie łączenia się do serwera\! -velocity.error.cant-connect=Nie udało się połączyć z serwerem {0}\: {1} -velocity.error.connecting-server-error=Nie udało się połączyć cię z serwerem {0}. Spróbuj ponownie później. -velocity.error.connected-server-error=Podczas łączenia cię z serwerem {0} wystąpił problem. +velocity.error.cant-connect=Nie udało się połączyć z serwerem \: +velocity.error.connecting-server-error=Nie udało się połączyć cię z serwerem . Spróbuj ponownie później. +velocity.error.connected-server-error=Podczas łączenia cię z serwerem wystąpił problem. velocity.error.internal-server-connection-error=Wystąpił wewnętrzny błąd połączenia serwera. velocity.error.logging-in-too-fast=Logujesz się zbyt szybko, spróbuj ponownie później. velocity.error.online-mode-only=Nie jesteś zalogowany do swojego konta Minecraft. Jeżeli jesteś zalogowany, spróbuj zrestartować grę. velocity.error.player-connection-error=Wystąpił wewnętrzny błąd w twoim połączeniu. velocity.error.modern-forwarding-needs-new-client=Ten serwer wspiera tylko wersje Minecraft 1.13 lub nowsze. velocity.error.modern-forwarding-failed=Twój serwer nie wysłał żądania o przekazanie danych do proxy. Upewnij się, że serwer jest skonfigurowany do nowoczesnego przekazywania Velocity. -velocity.error.moved-to-new-server=Zostałeś wyrzucony z {0}\: {1} +velocity.error.moved-to-new-server=Zostałeś wyrzucony z \: velocity.error.no-available-servers=Nie ma dostępnych serwerów, do których można cię połączyć. Spróbuj ponownie później lub skontaktuj się z administratorem. velocity.error.illegal-chat-characters=Niedozwolone znaki na czacie # Commands velocity.command.generic-error=Wystąpił błąd podczas wykonywania tego polecenia. velocity.command.command-does-not-exist=To polecenie nie istnieje. velocity.command.players-only=Tylko gracze mogą wykonać to polecenie. -velocity.command.server-does-not-exist=Wybrany serwer {0} nie istnieje. -velocity.command.player-not-found=Gracz {0} nie istnieje. -velocity.command.server-current-server=Jesteś obecnie połączony z {0}. +velocity.command.server-does-not-exist=Wybrany serwer nie istnieje. +velocity.command.player-not-found=Gracz nie istnieje. +velocity.command.server-current-server=Jesteś obecnie połączony z . velocity.command.server-too-many=Skonfigurowano zbyt wiele serwerów. Użyj autouzupełniania, aby wyświetlić wszystkie dostępne serwery. velocity.command.server-available=Dostępne serwery\: -velocity.command.server-tooltip-player-online={0} gracz online -velocity.command.server-tooltip-players-online={0} graczy online +velocity.command.server-tooltip-player-online= gracz online +velocity.command.server-tooltip-players-online= graczy online velocity.command.server-tooltip-current-server=Jesteś obecnie połączony z tym serwerem velocity.command.server-tooltip-offer-connect-server=Kliknij, aby połączyć się z tym serwerem -velocity.command.glist-player-singular=Z tym proxy jest obecnie połączony {0} gracz. -velocity.command.glist-player-plural=Z tym proxy jest obecnie połączonych {0} graczy. +velocity.command.glist-player-singular=Z tym proxy jest obecnie połączony gracz. +velocity.command.glist-player-plural=Z tym proxy jest obecnie połączonych graczy. velocity.command.glist-view-all=Aby zobaczyć wszystkich graczy na wszystkich serwerach, użyj /glist all. velocity.command.reload-success=Konfiguracja Velocity została pomyślnie załadowana ponownie. velocity.command.reload-failure=Nie udało się ponownie załadować twojej konfiguracji Velocity. Sprawdź konsolę po więcej szczegółów. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} jest objęty licencją na warunkach GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . jest objęty licencją na warunkach GNU General Public License v3. velocity.command.no-plugins=Obecnie nie ma zainstalowanych żadnych pluginów. -velocity.command.plugins-list=Pluginy\: {0} -velocity.command.plugin-tooltip-website=Strona internetowa\: {0} -velocity.command.plugin-tooltip-author=Autor\: {0} -velocity.command.plugin-tooltip-authors=Autorzy\: {0} +velocity.command.plugins-list=Pluginy\: +velocity.command.plugin-tooltip-website=Strona internetowa\: +velocity.command.plugin-tooltip-author=Autor\: +velocity.command.plugin-tooltip-authors=Autorzy\: velocity.command.dump-uploading=Przesyłanie zebranych informacji… velocity.command.dump-send-error=Wystąpił błąd podczas komunikacji z serwerami Velocity. Serwery mogą być chwilowo niedostępne lub wystąpił problem z ustawieniami sieci. Możesz znaleźć więcej informacji w logach lub konsoli swojego serwera Velocity. velocity.command.dump-success=Utworzono anonimowy raport zawierający przydatne informacje o tym proxy. Możesz udostępnić deweloperowi następujący odnośnik, jeśli o niego poprosił\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pt_BR.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pt_BR.properties index e0708942..39d697cc 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pt_BR.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pt_BR.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Você já está conectado a esse servidor\! velocity.error.already-connected-proxy=Você já está conectado a esse proxy\! velocity.error.already-connecting=Você já está tentando se conectar a um servidor\! -velocity.error.cant-connect=Não foi possível se conectar a {0}\: {1} -velocity.error.connecting-server-error=Não foi possível conectar você a {0}. Tente novamente mais tarde. -velocity.error.connected-server-error=Sua conexão com o servidor {0} encontrou um problema. +velocity.error.cant-connect=Não foi possível se conectar a \: +velocity.error.connecting-server-error=Não foi possível conectar você a . Tente novamente mais tarde. +velocity.error.connected-server-error=Sua conexão com o servidor encontrou um problema. velocity.error.internal-server-connection-error=Ocorreu um erro interno de conexão com o servidor. velocity.error.logging-in-too-fast=Você está entrando muito rápido, tente novamente mais tarde. velocity.error.online-mode-only=Você não está logado na sua conta do Minecraft. Se você já está logado na sua conta do Minecraft, tente reiniciar seu cliente do Minecraft. velocity.error.player-connection-error=Um erro interno ocorreu em sua conexão. velocity.error.modern-forwarding-needs-new-client=Este servidor é compatível apenas com o Minecraft 1.13 e superior. velocity.error.modern-forwarding-failed=Seu servidor não enviou uma solicitação de encaminhamento para o proxy. Certifique-se de que o servidor está configurado para encaminhamento Velocity. -velocity.error.moved-to-new-server=Você foi expulso de {0}\: {1} +velocity.error.moved-to-new-server=Você foi expulso de \: velocity.error.no-available-servers=Não há servidores disponíveis para conectá-lo. Tente novamente mais tarde ou contate um administrador. velocity.error.illegal-chat-characters=Caracteres não permitidos no chat # Commands velocity.command.generic-error=Ocorreu um erro ao executar este comando. velocity.command.command-does-not-exist=Esse comando não existe. velocity.command.players-only=Apenas jogadores podem executar esse comando. -velocity.command.server-does-not-exist=O servidor {0} não existe. -velocity.command.player-not-found=O jogador {0} não existe. -velocity.command.server-current-server=Você está atualmente conectado a {0}. +velocity.command.server-does-not-exist=O servidor não existe. +velocity.command.player-not-found=O jogador não existe. +velocity.command.server-current-server=Você está atualmente conectado a . velocity.command.server-too-many=Há muitos servidores configurados. Utilize o auto-preenchimento tab para ver todos os servidores disponíveis. velocity.command.server-available=Servidores disponíveis\: -velocity.command.server-tooltip-player-online={0} jogador conectado -velocity.command.server-tooltip-players-online={0} jogadores conectados +velocity.command.server-tooltip-player-online= jogador conectado +velocity.command.server-tooltip-players-online= jogadores conectados velocity.command.server-tooltip-current-server=Atualmente conectado a este servidor velocity.command.server-tooltip-offer-connect-server=Clique para conectar a este servidor -velocity.command.glist-player-singular=O jogador {0} está atualmente conectado ao proxy. -velocity.command.glist-player-plural={0} jogadores estão atualmente conectados ao proxy. +velocity.command.glist-player-singular=O jogador está atualmente conectado ao proxy. +velocity.command.glist-player-plural= jogadores estão atualmente conectados ao proxy. velocity.command.glist-view-all=Para ver todos os jogadores em todos os servidores, use /glist all. velocity.command.reload-success=Configuração do Velocity recarregada com êxito. velocity.command.reload-failure=Não foi possível recarregar a configuração do Velocity. Verifique o console para mais detalhes. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} está licenciado sobre os termos da Licença Pública Geral GNU v3. +velocity.command.version-copyright=Copyright 2018- . está licenciado sobre os termos da Licença Pública Geral GNU v3. velocity.command.no-plugins=Não há plugins instalados atualmente. -velocity.command.plugins-list=Plugins\: {0} -velocity.command.plugin-tooltip-website=Site\: {0} -velocity.command.plugin-tooltip-author=Autor\: {0} -velocity.command.plugin-tooltip-authors=Autores\: {0} +velocity.command.plugins-list=Plugins\: +velocity.command.plugin-tooltip-website=Site\: +velocity.command.plugin-tooltip-author=Autor\: +velocity.command.plugin-tooltip-authors=Autores\: velocity.command.dump-uploading=Enviando informações coletadas... velocity.command.dump-send-error=Ocorreu um erro ao se comunicar com os servidores Velocity. Os servidores podem estar temporariamente indisponíveis ou há um problema com suas configurações de rede. Você pode encontrar mais informações no log ou no console de seu servidor Velocity. velocity.command.dump-success=Foi criado um relatório anônimo contendo informações úteis sobre este proxy. Se um desenvolvedor o solicitou, você pode compartilhar o seguinte link com eles\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ro_RO.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ro_RO.properties index ea0d4609..e3d28fce 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ro_RO.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ro_RO.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=You are already connected to this server\! velocity.error.already-connected-proxy=You are already connected to this proxy\! velocity.error.already-connecting=You are already trying to connect to a server\! -velocity.error.cant-connect=Unable to connect to {0}\: {1} -velocity.error.connecting-server-error=Unable to connect you to {0}. Please try again later. -velocity.error.connected-server-error=Your connection to {0} encountered a problem. +velocity.error.cant-connect=Unable to connect to \: +velocity.error.connecting-server-error=Unable to connect you to . Please try again later. +velocity.error.connected-server-error=Your connection to encountered a problem. velocity.error.internal-server-connection-error=An internal server connection error occurred. velocity.error.logging-in-too-fast=You are logging in too fast, try again later. velocity.error.online-mode-only=You are not logged into your Minecraft account. If you are logged into your Minecraft account, try restarting your Minecraft client. velocity.error.player-connection-error=An internal error occurred in your connection. velocity.error.modern-forwarding-needs-new-client=This server is only compatible with Minecraft 1.13 and above. velocity.error.modern-forwarding-failed=Your server did not send a forwarding request to the proxy. Make sure the server is configured for Velocity forwarding. -velocity.error.moved-to-new-server=You were kicked from {0}\: {1} +velocity.error.moved-to-new-server=You were kicked from \: velocity.error.no-available-servers=There are no available servers to connect you to. Try again later or contact an admin. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=An error occurred while running this command. velocity.command.command-does-not-exist=This command does not exist. velocity.command.players-only=Only players can run this command. -velocity.command.server-does-not-exist=The specified server {0} does not exist. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=You are currently connected to {0}. +velocity.command.server-does-not-exist=The specified server does not exist. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=You are currently connected to . velocity.command.server-too-many=There are too many servers set up. Use tab completion to view all servers available. velocity.command.server-available=Available servers\: -velocity.command.server-tooltip-player-online={0} player online -velocity.command.server-tooltip-players-online={0} players online +velocity.command.server-tooltip-player-online= player online +velocity.command.server-tooltip-players-online= players online velocity.command.server-tooltip-current-server=Currently connected to this server velocity.command.server-tooltip-offer-connect-server=Click to connect to this server -velocity.command.glist-player-singular={0} player is currently connected to the proxy. -velocity.command.glist-player-plural={0} players are currently connected to the proxy. +velocity.command.glist-player-singular= player is currently connected to the proxy. +velocity.command.glist-player-plural= players are currently connected to the proxy. velocity.command.glist-view-all=To view all players on servers, use /glist all. velocity.command.reload-success=Velocity configuration successfully reloaded. velocity.command.reload-failure=Unable to reload your Velocity configuration. Check the console for more details. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=There are no plugins currently installed. -velocity.command.plugins-list=Plugins\: {0} -velocity.command.plugin-tooltip-website=Website\: {0} -velocity.command.plugin-tooltip-author=Author\: {0} -velocity.command.plugin-tooltip-authors=Authors\: {0} +velocity.command.plugins-list=Plugins\: +velocity.command.plugin-tooltip-website=Website\: +velocity.command.plugin-tooltip-author=Author\: +velocity.command.plugin-tooltip-authors=Authors\: velocity.command.dump-uploading=Uploading gathered information... velocity.command.dump-send-error=An error occurred while communicating with the Velocity servers. The servers may be temporarily unavailable or there is an issue with your network settings. You can find more information in the log or console of your Velocity server. velocity.command.dump-success=Created an anonymised report containing useful information about this proxy. If a developer requested it, you may share the following link with them\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ru_RU.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ru_RU.properties index 5e5be8e6..8681198f 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ru_RU.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ru_RU.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Вы уже подключены к этому серверу\! velocity.error.already-connected-proxy=Игрок с таким ником уже играет на сервере\! velocity.error.already-connecting=Вы уже подключаетесь к серверу\! -velocity.error.cant-connect=Не удалось подключиться к серверу {0}\: {1} -velocity.error.connecting-server-error=Не удалось подключить вас к серверу {0}. Пожалуйста, попробуйте снова через некоторое время. -velocity.error.connected-server-error=С вашим подключением к серверу {0} возникла проблема. +velocity.error.cant-connect=Не удалось подключиться к серверу \: +velocity.error.connecting-server-error=Не удалось подключить вас к серверу . Пожалуйста, попробуйте снова через некоторое время. +velocity.error.connected-server-error=С вашим подключением к серверу возникла проблема. velocity.error.internal-server-connection-error=На сервере произошла внутренняя ошибка подключения. velocity.error.logging-in-too-fast=Вы входите слишком быстро, попробуйте снова через некоторое время. velocity.error.online-mode-only=Вы не вошли в свой аккаунт Minecraft. Если вы уверены, что вошли в аккаунт, попробуйте перезапустить свой клиент Minecraft. velocity.error.player-connection-error=В вашем подключении произошла внутренняя ошибка. velocity.error.modern-forwarding-needs-new-client=Этот сервер совместим только с Minecraft 1.13 и выше. velocity.error.modern-forwarding-failed=Ваш сервер не посылал запрос на переадресацию на прокси-сервер. Убедитесь, что сервер настроен на переадресацию Velocity. -velocity.error.moved-to-new-server=Вы были кикнуты с сервера {0}\: {1} +velocity.error.moved-to-new-server=Вы были кикнуты с сервера \: velocity.error.no-available-servers=Нет серверов, доступных для подключения. Попробуйте позже или свяжитесь с администратором. velocity.error.illegal-chat-characters=Недопустимые символы в чате # Commands velocity.command.generic-error=Во время выполнения этой команды произошла ошибка. velocity.command.command-does-not-exist=Этой команды не существует. velocity.command.players-only=Только игроки могут использовать эту команду. -velocity.command.server-does-not-exist=Указанный сервер {0} не существует. -velocity.command.player-not-found=Указанный игрок {0} не существует. -velocity.command.server-current-server=На данный момент вы подключены к серверу {0}. +velocity.command.server-does-not-exist=Указанный сервер не существует. +velocity.command.player-not-found=Указанный игрок не существует. +velocity.command.server-current-server=На данный момент вы подключены к серверу . velocity.command.server-too-many=Настроено слишком много серверов. Для просмотра всех доступных серверов, используйте автозаполнение клавишей Tab. velocity.command.server-available=Доступные серверы\: -velocity.command.server-tooltip-player-online={0} игрок онлайн -velocity.command.server-tooltip-players-online={0} игрок(а, ов) онлайн +velocity.command.server-tooltip-player-online= игрок онлайн +velocity.command.server-tooltip-players-online= игрок(а, ов) онлайн velocity.command.server-tooltip-current-server=Подключен к этому серверу velocity.command.server-tooltip-offer-connect-server=Кликните, чтобы присоединиться к этому серверу -velocity.command.glist-player-singular={0} игрок подключен к прокси на данный момент. -velocity.command.glist-player-plural={0} игрок(а, ов) подключены к прокси на данный момент. +velocity.command.glist-player-singular= игрок подключен к прокси на данный момент. +velocity.command.glist-player-plural= игрок(а, ов) подключены к прокси на данный момент. velocity.command.glist-view-all=Чтобы просмотреть всех игроков на серверах, используйте /glist all. velocity.command.reload-success=Конфигурация Velocity успешно перезагружена. velocity.command.reload-failure=Не удалось перезагрузить конфигурацию Velocity. Проверьте консоль для получения более подробной информации. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} лицензирована на условиях GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . лицензирована на условиях GNU General Public License v3. velocity.command.no-plugins=Ни одного плагина не установлено. -velocity.command.plugins-list=Плагины\: {0} -velocity.command.plugin-tooltip-website=Веб-сайт\: {0} -velocity.command.plugin-tooltip-author=Автор\: {0} -velocity.command.plugin-tooltip-authors=Авторы\: {0} +velocity.command.plugins-list=Плагины\: +velocity.command.plugin-tooltip-website=Веб-сайт\: +velocity.command.plugin-tooltip-author=Автор\: +velocity.command.plugin-tooltip-authors=Авторы\: velocity.command.dump-uploading=Загрузка полученной информации... velocity.command.dump-send-error=Произошла ошибка при попытке связаться с серверами Velocity. Возможно, серверы временно недоступны или ваша сеть настроена неправильно. Более подробную информацию можно найти в логах или консоли вашего сервера Velocity. velocity.command.dump-success=Создан анонимный отчет, содержащий полезную информацию об этом прокси. Если этот отчёт запросил разработчик, вы можете поделиться с ним следующей ссылкой\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sk_SK.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sk_SK.properties index 04e75974..d1d8028b 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sk_SK.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sk_SK.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Na tento server si už pripojený\! velocity.error.already-connected-proxy=Na tento proxy server si už pripojený\! velocity.error.already-connecting=Už sa pokúšaš o pripojenie na server\! -velocity.error.cant-connect=Nepodarilo sa pripojiť na {0}\: {1} -velocity.error.connecting-server-error=Nepodarilo sa pripojiť ťa na {0}. Skús to prosím neskôr. -velocity.error.connected-server-error=V tvojom pripojení na {0} sa vyskytla chyba. +velocity.error.cant-connect=Nepodarilo sa pripojiť na \: +velocity.error.connecting-server-error=Nepodarilo sa pripojiť ťa na . Skús to prosím neskôr. +velocity.error.connected-server-error=V tvojom pripojení na sa vyskytla chyba. velocity.error.internal-server-connection-error=V serverovom pripojení sa vyskytla interná chyba. velocity.error.logging-in-too-fast=Pripájaš sa príliš rýchlo, skús to znova neskôr. velocity.error.online-mode-only=Nie si prihlásený do svojho Minecraft účtu. Ak si prihlásený, skús reštartovať svoj Minecraft klient. velocity.error.player-connection-error=V tvojom pripojení sa vyskytla interná chyba. velocity.error.modern-forwarding-needs-new-client=Tento server je kompatibilný len s Minecraftom 1.13 a novším. velocity.error.modern-forwarding-failed=Tvoj server neodoslal presmerovaciu požiadavku proxy serveru. Uisti sa, že server je nastavený na presmerovanie Velocity. -velocity.error.moved-to-new-server=Bol si odpojený z {0}\: {1} +velocity.error.moved-to-new-server=Bol si odpojený z \: velocity.error.no-available-servers=Nie sú k dispozícii žiadne servery na pripojenie. Skús to neskôr alebo kontaktuj admina. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=Nastala chyba pri vykonávaní tohto príkazu. velocity.command.command-does-not-exist=Tento príkaz neexistuje. velocity.command.players-only=Len hráči môžu vykonať tento príkaz. -velocity.command.server-does-not-exist=Zadaný server {0} neexistuje. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Aktuálne si pripojený na {0}. +velocity.command.server-does-not-exist=Zadaný server neexistuje. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Aktuálne si pripojený na . velocity.command.server-too-many=Je nastavených príliš veľa serverov. Použi tab pre zobrazenie dostupných serverov. velocity.command.server-available=Dostupné servery\: -velocity.command.server-tooltip-player-online={0} hráč online -velocity.command.server-tooltip-players-online={0} hráčov online +velocity.command.server-tooltip-player-online= hráč online +velocity.command.server-tooltip-players-online= hráčov online velocity.command.server-tooltip-current-server=Aktuálne si pripojený na tento server velocity.command.server-tooltip-offer-connect-server=Klikni pre pripojenie na tento server -velocity.command.glist-player-singular={0} hráč je pripojený na tento proxy server. -velocity.command.glist-player-plural={0} hráčov je pripojených na tento proxy server. +velocity.command.glist-player-singular= hráč je pripojený na tento proxy server. +velocity.command.glist-player-plural= hráčov je pripojených na tento proxy server. velocity.command.glist-view-all=Pre zobrazenie všetkých hráčov na všetkých serveroch použi /glist all. velocity.command.reload-success=Konfigurácia Velocity úspešne načítaná. velocity.command.reload-failure=Nepodarilo sa načítať konfiguráciu Velocity. Pozri sa do konzoly pre viac informácií. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} je licencovaný pod podmienkami GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . je licencovaný pod podmienkami GNU General Public License v3. velocity.command.no-plugins=Aktuálne nie sú nainštalované žiadne pluginy. -velocity.command.plugins-list=Pluginy\: {0} -velocity.command.plugin-tooltip-website=Webstránka\: {0} -velocity.command.plugin-tooltip-author=Autor\: {0} -velocity.command.plugin-tooltip-authors=Autori\: {0} +velocity.command.plugins-list=Pluginy\: +velocity.command.plugin-tooltip-website=Webstránka\: +velocity.command.plugin-tooltip-author=Autor\: +velocity.command.plugin-tooltip-authors=Autori\: velocity.command.dump-uploading=Nahrávanie získaných informácií... velocity.command.dump-send-error=Nastala chyba pri komunikácii s Velocity servermi. Servery môžu byť dočasne nedostupné alebo je chyba v prístupe na internet. Viac informácií je v logu a v konzole tvojho Velocity servera. velocity.command.dump-success=Bol vytvorený anonymný záznam o tomto serveri. Ak si ho vyžiadal developer, môžeš mu poslať nasledujúci odkaz\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sq_AL.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sq_AL.properties index 711b8e84..fd2ed9c6 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sq_AL.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sq_AL.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Ju tashmë jeni lidhur me këtë server\! velocity.error.already-connected-proxy=Ju jeni lidhur tashmë me këtë përfaqësues\! velocity.error.already-connecting=Ju tashmë po përpiqeni të lidheni me një server\! -velocity.error.cant-connect=Nuk mund të lidhet me {0}\: {1} -velocity.error.connecting-server-error=Nuk mund të të lidh me {0}. Ju lutemi provoni përsëri më vonë. -velocity.error.connected-server-error=Lidhja juaj me {0} hasi një problem. +velocity.error.cant-connect=Nuk mund të lidhet me \: +velocity.error.connecting-server-error=Nuk mund të të lidh me . Ju lutemi provoni përsëri më vonë. +velocity.error.connected-server-error=Lidhja juaj me hasi një problem. velocity.error.internal-server-connection-error=Ndodhi një gabim i brendshëm i lidhjes së serverit. velocity.error.logging-in-too-fast=Po hyni shumë shpejt, provoni përsëri më vonë. velocity.error.online-mode-only=Ju nuk jeni regjistruar në llogarinë tuaj në Minecraft. Nëse jeni regjistruar në llogarinë tuaj Minecraft, provoni të rindizni klientin tuaj Minecraft. velocity.error.player-connection-error=Ndodhi një gabim i brendshëm në lidhjen tuaj. velocity.error.modern-forwarding-needs-new-client=Ky server është i pajtueshëm vetëm me Minecraft 1.13 e lart. velocity.error.modern-forwarding-failed=Serveri juaj nuk i dërgoi një kërkesë për transferim përfaqësuesit. Sigurohuni që serveri të jetë konfiguruar për transferimin e shpejtësisë. -velocity.error.moved-to-new-server=Ju kanë dëbuar nga {0}\: {1} +velocity.error.moved-to-new-server=Ju kanë dëbuar nga \: velocity.error.no-available-servers=Nuk ka servera të disponueshëm për t'ju lidhur. Provo përsëri më vonë ose kontakto një administrator. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=Ndodhi një gabim gjatë ekzekutimit të kësaj komande. velocity.command.command-does-not-exist=Kjo komandë nuk ekziston. velocity.command.players-only=Vetëm lojtarët mund ta drejtojnë këtë komandë. -velocity.command.server-does-not-exist=Serveri i specifikuar {0} nuk ekziston. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Aktualisht jeni i lidhur me {0}. +velocity.command.server-does-not-exist=Serveri i specifikuar nuk ekziston. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Aktualisht jeni i lidhur me . velocity.command.server-too-many=Ka shumë servera të konfiguruar. Përdorni përfundimin e skedave për të parë të gjithë serverat e disponueshëm. velocity.command.server-available=Serverat e disponueshëm\: -velocity.command.server-tooltip-player-online={0} lojtar në internet -velocity.command.server-tooltip-players-online={0} lojtarë në internet +velocity.command.server-tooltip-player-online= lojtar në internet +velocity.command.server-tooltip-players-online= lojtarë në internet velocity.command.server-tooltip-current-server=Aktualisht e lidhur me këtë server velocity.command.server-tooltip-offer-connect-server=Klikoni për t'u lidhur me këtë server -velocity.command.glist-player-singular={0} lojtari aktualisht është i lidhur me përfaqësuesin. -velocity.command.glist-player-plural={0} lojtarët janë aktualisht të lidhur me përfaqësuesin. +velocity.command.glist-player-singular= lojtari aktualisht është i lidhur me përfaqësuesin. +velocity.command.glist-player-plural= lojtarët janë aktualisht të lidhur me përfaqësuesin. velocity.command.glist-view-all=Për të parë të gjithë lojtarët në servera, përdorni /glist all. velocity.command.reload-success=Konfigurimi i shpejtësisë u ringarkua me sukses. velocity.command.reload-failure=Nuk mund të ringarkohet konfigurimi i shpejtësisë. Kontrolloni konsolën për më shumë detaje. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=There are no plugins currently installed. -velocity.command.plugins-list=Plugins\: {0} -velocity.command.plugin-tooltip-website=Website\: {0} -velocity.command.plugin-tooltip-author=Author\: {0} -velocity.command.plugin-tooltip-authors=Authors\: {0} +velocity.command.plugins-list=Plugins\: +velocity.command.plugin-tooltip-website=Website\: +velocity.command.plugin-tooltip-author=Author\: +velocity.command.plugin-tooltip-authors=Authors\: velocity.command.dump-uploading=Uploading gathered information... velocity.command.dump-send-error=An error occurred while communicating with the Velocity servers. The servers may be temporarily unavailable or there is an issue with your network settings. You can find more information in the log or console of your Velocity server. velocity.command.dump-success=Created an anonymised report containing useful information about this proxy. If a developer requested it, you may share the following link with them\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_CS.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_CS.properties index 006a3037..5433347b 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_CS.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_CS.properties @@ -18,40 +18,40 @@ velocity.error.already-connected=Već ste povezani na ovaj server\! velocity.error.already-connected-proxy=Već ste povezani na ovaj proxy\! velocity.error.already-connecting=Već pokušavate da se povežete na server\! -velocity.error.cant-connect=Nije moguće povezati se na {0}\: {1} -velocity.error.connecting-server-error=Nije moguće povezati Vas na {0}. Molimo pokušajte ponovo kasnije. -velocity.error.connected-server-error=Došlo je do problema sa Vašom vezom sa {0}. +velocity.error.cant-connect=Nije moguće povezati se na \: +velocity.error.connecting-server-error=Nije moguće povezati Vas na . Molimo pokušajte ponovo kasnije. +velocity.error.connected-server-error=Došlo je do problema sa Vašom vezom sa . velocity.error.internal-server-connection-error=Dogodila se interna greška sa konekcijom sa serverom. velocity.error.logging-in-too-fast=Logujete se prebrzo, molimo pokušajte kasnije. velocity.error.online-mode-only=Niste ulogovani na Vaš Minecraft nalog. Ako ipak jeste, probajte da restartujete Minecraft klijent. velocity.error.player-connection-error=Dogodila se interna greška sa Vašom konekcijom. velocity.error.modern-forwarding-needs-new-client=Ovaj server je kompatibilan samo sa Minecraft-om 1.13 i višim verzijama. velocity.error.modern-forwarding-failed=Vaš server nije poslao zahtev za prosleđivanje na proxy. Proverite je li server konfigurisan za Velocity prosleđivanje. -velocity.error.moved-to-new-server=Izbačeni ste iz {0}\: {1} +velocity.error.moved-to-new-server=Izbačeni ste iz \: velocity.error.no-available-servers=Nema dostupnih servera za povezati Vas. Pokušajte ponovo kasnije ili kontaktirajte nekog admina. # Commands velocity.command.generic-error=Došlo je do greške pri pokretanju te komande. velocity.command.command-does-not-exist=Ta komanda ne postoji. velocity.command.players-only=Tu komandu mogu pokrenuti samo igrači. -velocity.command.server-does-not-exist=Navedeni server {0} ne postoji. -velocity.command.server-current-server=Trenutno ste povezani na {0}. +velocity.command.server-does-not-exist=Navedeni server ne postoji. +velocity.command.server-current-server=Trenutno ste povezani na . velocity.command.server-too-many=Postoji mnogo servera. Koristite tab dovršavanje za pregled svih dostupnih servera. velocity.command.server-available=Dostupni serveri\: -velocity.command.server-tooltip-player-online={0} igrač online -velocity.command.server-tooltip-players-online={0} igrača online +velocity.command.server-tooltip-player-online= igrač online +velocity.command.server-tooltip-players-online= igrača online velocity.command.server-tooltip-current-server=Trenutno ste povezani na ovaj server velocity.command.server-tooltip-offer-connect-server=Kliknite za povezivanje na ovaj server -velocity.command.glist-player-singular={0} igrač je trenutno povezan na proxy. -velocity.command.glist-player-plural={0} igrača je trenutno povezano na proxy. +velocity.command.glist-player-singular= igrač je trenutno povezan na proxy. +velocity.command.glist-player-plural= igrača je trenutno povezano na proxy. velocity.command.glist-view-all=Za pregled svih igrača na serverima koristite /glist all. velocity.command.reload-success=Velocity konfiguracija uspešno ponovno učitana. velocity.command.reload-failure=Nije moguće ponovo učitati Vašu Velocity konfiguraciju. Proverite konzolu za više detalja. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} je licenciran pod uslovima licence GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . je licenciran pod uslovima licence GNU General Public License v3. velocity.command.no-plugins=Trenutno nema instaliranih plugin-a. -velocity.command.plugins-list=Plugin-i\: {0} -velocity.command.plugin-tooltip-website=Web stranica\: {0} -velocity.command.plugin-tooltip-author=Autor\: {0} -velocity.command.plugin-tooltip-authors=Autori\: {0} +velocity.command.plugins-list=Plugin-i\: +velocity.command.plugin-tooltip-website=Web stranica\: +velocity.command.plugin-tooltip-author=Autor\: +velocity.command.plugin-tooltip-authors=Autori\: velocity.command.dump-uploading=Otpremanje prikupljenih informacija... velocity.command.dump-send-error=Dogodila se greška pri komunikaciji sa Velocity serverima. Serveri su ili trenutno nedostupni, ili postoji problem sa Vašim mrežnim podešavanjima. Možete saznati više informacija u log-u ili konzoli Vašeg Velocity servera. velocity.command.dump-success=Kreiran anonimiziran izveštaj koji sadrži korisne informacije o ovom proxy-u. Ako ga developer zatraži, možete mu poslati ovaj link\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_Latn.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_Latn.properties index 6106bb7b..fe1731bb 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_Latn.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_Latn.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Već ste povezani na ovaj server\! velocity.error.already-connected-proxy=Već ste povezani na ovaj proxy\! velocity.error.already-connecting=Već pokušavate da se povežete na server\! -velocity.error.cant-connect=Nije moguće povezati se na {0}\: {1} -velocity.error.connecting-server-error=Nije moguće povezati Vas na {0}. Molimo pokušajte ponovo kasnije. -velocity.error.connected-server-error=Došlo je do problema sa Vašom vezom sa {0}. +velocity.error.cant-connect=Nije moguće povezati se na \: +velocity.error.connecting-server-error=Nije moguće povezati Vas na . Molimo pokušajte ponovo kasnije. +velocity.error.connected-server-error=Došlo je do problema sa Vašom vezom sa . velocity.error.internal-server-connection-error=Dogodila se interna greška sa konekcijom sa serverom. velocity.error.logging-in-too-fast=Logujete se prebrzo, molimo pokušajte kasnije. velocity.error.online-mode-only=Niste ulogovani na Vaš Minecraft nalog. Ako ipak jeste, probajte da restartujete Minecraft klijent. velocity.error.player-connection-error=Dogodila se interna greška sa Vašom konekcijom. velocity.error.modern-forwarding-needs-new-client=Ovaj server je kompatibilan samo sa Minecraft-om 1.13 i višim verzijama. velocity.error.modern-forwarding-failed=Vaš server nije poslao zahtev za prosleđivanje na proxy. Proverite je li server konfigurisan za Velocity prosleđivanje. -velocity.error.moved-to-new-server=Izbačeni ste iz {0}\: {1} +velocity.error.moved-to-new-server=Izbačeni ste iz \: velocity.error.no-available-servers=Nema dostupnih servera za povezati Vas. Pokušajte ponovo kasnije ili kontaktirajte nekog admina. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=Došlo je do greške pri pokretanju te komande. velocity.command.command-does-not-exist=Ta komanda ne postoji. velocity.command.players-only=Tu komandu mogu pokrenuti samo igrači. -velocity.command.server-does-not-exist=Navedeni server {0} ne postoji. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Trenutno ste povezani na {0}. +velocity.command.server-does-not-exist=Navedeni server ne postoji. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Trenutno ste povezani na . velocity.command.server-too-many=Postoji mnogo servera. Koristite tab dovršavanje za pregled svih dostupnih servera. velocity.command.server-available=Dostupni serveri\: -velocity.command.server-tooltip-player-online={0} igrač online -velocity.command.server-tooltip-players-online={0} igrača online +velocity.command.server-tooltip-player-online= igrač online +velocity.command.server-tooltip-players-online= igrača online velocity.command.server-tooltip-current-server=Trenutno ste povezani na ovaj server velocity.command.server-tooltip-offer-connect-server=Kliknite za povezivanje na ovaj server -velocity.command.glist-player-singular={0} igrač je trenutno povezan na proxy. -velocity.command.glist-player-plural={0} igrača je trenutno povezano na proxy. +velocity.command.glist-player-singular= igrač je trenutno povezan na proxy. +velocity.command.glist-player-plural= igrača je trenutno povezano na proxy. velocity.command.glist-view-all=Za pregled svih igrača na serverima koristite /glist all. velocity.command.reload-success=Velocity konfiguracija uspešno ponovno učitana. velocity.command.reload-failure=Nije moguće ponovo učitati Vašu Velocity konfiguraciju. Proverite konzolu za više detalja. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=Trenutno nema instaliranih plugin-a. -velocity.command.plugins-list=Plugin-i\: {0} -velocity.command.plugin-tooltip-website=Web stranica\: {0} -velocity.command.plugin-tooltip-author=Autor\: {0} -velocity.command.plugin-tooltip-authors=Autori\: {0} +velocity.command.plugins-list=Plugin-i\: +velocity.command.plugin-tooltip-website=Web stranica\: +velocity.command.plugin-tooltip-author=Autor\: +velocity.command.plugin-tooltip-authors=Autori\: velocity.command.dump-uploading=Otpremanje prikupljenih informacija... velocity.command.dump-send-error=Dogodila se greška pri komunikaciji sa Velocity serverima. Serveri su ili trenutno nedostupni, ili postoji problem sa Vašim mrežnim podešavanjima. Možete saznati više informacija u log-u ili konzoli Vašeg Velocity servera. velocity.command.dump-success=Kreiran anonimiziran izveštaj koji sadrži korisne informacije o ovom proxy-u. Ako ga developer zatraži, možete mu poslati ovaj link\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sv_SE.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sv_SE.properties index 973506eb..0020b954 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sv_SE.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sv_SE.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Du är redan ansluten till den här servern\! velocity.error.already-connected-proxy=Du är redan ansluten till den här proxyn\! velocity.error.already-connecting=Du försöker redan ansluta till en server\! -velocity.error.cant-connect=Det går inte att ansluta till {0}\: {1} -velocity.error.connecting-server-error=Det går inte att ansluta dig till {0}. Snälla försök igen senare. -velocity.error.connected-server-error=Din anslutning till {0} stötte på ett problem. +velocity.error.cant-connect=Det går inte att ansluta till \: +velocity.error.connecting-server-error=Det går inte att ansluta dig till . Snälla försök igen senare. +velocity.error.connected-server-error=Din anslutning till stötte på ett problem. velocity.error.internal-server-connection-error=Ett internt serveranslutningsfel uppstod. velocity.error.logging-in-too-fast=Du loggar in för snabbt, Försök igen senare. velocity.error.online-mode-only=Du är inte inloggad på ditt Minecraft konto. Om du är inloggad på ditt Minecraft konto, prova att starta om din Minecraft klient. velocity.error.player-connection-error=Ett internt fel uppstod i din anslutning. velocity.error.modern-forwarding-needs-new-client=Denna server är endast kompatibel med Minecraft 1.13 och högre. velocity.error.modern-forwarding-failed=Din server skickade inte en vidarebefordrings begäran till proxyn. Se till att servern är konfigurerad för Velocity vidarebefordrings begäran. -velocity.error.moved-to-new-server=Du blev sparkad från {0}\: {1} +velocity.error.moved-to-new-server=Du blev sparkad från \: velocity.error.no-available-servers=Det finns inga tillgängliga servrar att ansluta dig till. Försök igen senare eller kontakta en admin. velocity.error.illegal-chat-characters=Olagliga tecken i chatten # Commands velocity.command.generic-error=Ett fel uppstod när det här kommandot kördes. velocity.command.command-does-not-exist=Detta kommando finns inte. velocity.command.players-only=Bara spelare kan använda detta kommando. -velocity.command.server-does-not-exist=Den angivna servern {0} finns inte. -velocity.command.player-not-found=Den angivna spelaren {0} finns inte. -velocity.command.server-current-server=Du är just nu ansluten till {0}. +velocity.command.server-does-not-exist=Den angivna servern finns inte. +velocity.command.player-not-found=Den angivna spelaren finns inte. +velocity.command.server-current-server=Du är just nu ansluten till . velocity.command.server-too-many=Det finns för många servrar som är konfigurerade. Använd tab färdigställandet för att se alla tillgängliga servrar. velocity.command.server-available=Tillgängliga servrar\: -velocity.command.server-tooltip-player-online={0} spelare online -velocity.command.server-tooltip-players-online={0} spelare online +velocity.command.server-tooltip-player-online= spelare online +velocity.command.server-tooltip-players-online= spelare online velocity.command.server-tooltip-current-server=Just nu ansluten till denna servern velocity.command.server-tooltip-offer-connect-server=Klicka för att ansluta till servern -velocity.command.glist-player-singular={0} spelare är just nu ansluten till proxyn. -velocity.command.glist-player-plural={0} spelare är just nu ansluten till proxyn. +velocity.command.glist-player-singular= spelare är just nu ansluten till proxyn. +velocity.command.glist-player-plural= spelare är just nu ansluten till proxyn. velocity.command.glist-view-all=För att se alla spelare på servrar, använd /glist all. velocity.command.reload-success=Velocity konfigurationen har laddats om. velocity.command.reload-failure=Det gick inte att ladda om din Velocity konfiguration. Kontrollera konsolen för mer information. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} är licensierad enligt villkoren i GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . är licensierad enligt villkoren i GNU General Public License v3. velocity.command.no-plugins=Det finns inga tillägg installerade. -velocity.command.plugins-list=Tillägg\: {0} -velocity.command.plugin-tooltip-website=Webbsida\: {0} -velocity.command.plugin-tooltip-author=Utgivare\: {0} -velocity.command.plugin-tooltip-authors=Utgivare\: {0} +velocity.command.plugins-list=Tillägg\: +velocity.command.plugin-tooltip-website=Webbsida\: +velocity.command.plugin-tooltip-author=Utgivare\: +velocity.command.plugin-tooltip-authors=Utgivare\: velocity.command.dump-uploading=Laddar upp insamlad information... velocity.command.dump-send-error=Ett fel uppstod under kommunikationen med Velocity servrarna. Servrarna kan vara tillfälligt otillgängliga eller så finns det ett problem med dina nätverksinställningar. Du kan hitta mer information i loggen eller konsolen på din Velocity server. velocity.command.dump-success=Skapade en anonymiserad rapport med användbar information om denna proxy. Om en utvecklare begärde det, kan du dela följande länk med dem\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tl_PH.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tl_PH.properties index ec4cc9d2..053013f5 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tl_PH.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tl_PH.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Nakakonekta ka na sa serbidor na ito\! velocity.error.already-connected-proxy=Nakakonekta ka na sa proxy na ito\! velocity.error.already-connecting=Sinusubukan mo na makakonekta sa isang serbidor\! -velocity.error.cant-connect=Hindi makakonekta sa {0} dahil {1} -velocity.error.connecting-server-error=Hindi ka makakonekta sa {0}. Paki-ulitin mo ulit na mamaya. -velocity.error.connected-server-error=Maencounter isang problema ang koneksyon mo sa {0}. +velocity.error.cant-connect=Hindi makakonekta sa dahil +velocity.error.connecting-server-error=Hindi ka makakonekta sa . Paki-ulitin mo ulit na mamaya. +velocity.error.connected-server-error=Maencounter isang problema ang koneksyon mo sa . velocity.error.internal-server-connection-error=Nangyari isang internal server error. velocity.error.logging-in-too-fast=Masyadong mabilis ang mga attempt ng pagpapatunay mo. Paki-ulitin mo ulit na mamaya. velocity.error.online-mode-only=Hindi nakapagpatunay sa akawnt ng Minecraft mo. Kung nakapatunayan sa akawnt ng Minecraft mo, tapos sinubukan i-restart mo ang klient ng Minecraft niyo. velocity.error.player-connection-error=Nangyari isang internal server error sa ng koneksiyon mo. velocity.error.modern-forwarding-needs-new-client=Ang server na ito magkasundo lang kasama ang version 1.13 at sa itaas. velocity.error.modern-forwarding-failed=Hindi ipadala ang serbidor mo ng forwarding request sa proxy. Dapat nae-ensure mo ng naka-configure ang server para ng forwarding ng Velocity. -velocity.error.moved-to-new-server=Nanipa ka mula sa {0} dahil {1} +velocity.error.moved-to-new-server=Nanipa ka mula sa dahil velocity.error.no-available-servers=Wala na ang mga serbidor na magagamit para sa'yo. Paki-ulitin mo ulit o magcontact ka isang admin. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=Nangyari isang pagkakamali kapag sinubukan ng serbidor magamit ang komando ng ito. velocity.command.command-does-not-exist=Walang komando sa serbidor. velocity.command.players-only=Mga manlalaro lang makagamit ang komando ito. -velocity.command.server-does-not-exist=Walang serbidor {0}. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Nakakonekta ka sa {0}. +velocity.command.server-does-not-exist=Walang serbidor . +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Nakakonekta ka sa . velocity.command.server-too-many=Meron masyadong serbidor sa konfig. Magamit ka ng tab completion sa makita lahat ng serbidor na magagamit. velocity.command.server-available=Magagamit na mga serbidor\: -velocity.command.server-tooltip-player-online={0} manlalaro sa online -velocity.command.server-tooltip-players-online={0} mga manlalaro sa online +velocity.command.server-tooltip-player-online= manlalaro sa online +velocity.command.server-tooltip-players-online= mga manlalaro sa online velocity.command.server-tooltip-current-server=Nakakonekta ka sa serbidor na ito velocity.command.server-tooltip-offer-connect-server=I-click dito sa kumonekta sa serbidor na ito -velocity.command.glist-player-singular={0} manlalaro nasa proxy. -velocity.command.glist-player-plural={0} mga manlalaro nasa proxy. +velocity.command.glist-player-singular= manlalaro nasa proxy. +velocity.command.glist-player-plural= mga manlalaro nasa proxy. velocity.command.glist-view-all=Para sa makita ang mga lahat ng manlalaro sa mga serbidor, magamit ka ng /glist all. velocity.command.reload-success=Matagumpay ang reload ng konfigurasyon ng Velocity. velocity.command.reload-failure=Hindi kaya ang magreload ng konfigurasyon mo sa Velocity. Magsiyasat ang konsole para mas maraming detalye. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=Walang plugin nang naka-install. -velocity.command.plugins-list=Mga plugin\: {0} -velocity.command.plugin-tooltip-website=Website\: {0} -velocity.command.plugin-tooltip-author=Autor\: {0} -velocity.command.plugin-tooltip-authors=Mga autor\: {0} +velocity.command.plugins-list=Mga plugin\: +velocity.command.plugin-tooltip-website=Website\: +velocity.command.plugin-tooltip-author=Autor\: +velocity.command.plugin-tooltip-authors=Mga autor\: velocity.command.dump-uploading=Inu-upload ang ipong impormasyon... velocity.command.dump-send-error=Nangyari isang pagkakamali sa panahon ng koneksiyon kasama ang mga serbidor ng Velocity. Baka hindi magagamit na pansamantalang ang mga serbidor o may isang isyu sa mga network settings mo. Hinapin mo mas impormasyon sa log o console sa serbidor ng Velocity mo. velocity.command.dump-success=Lumikha isang report ng hindi nagpapakilala ng may impormasyon mahalaga tungkol sa proxy ng ito. Kung hingin ito ng developer, makashare ka ang link na ito kasama siya\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tr_TR.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tr_TR.properties index 3c255d93..4bc6ebf3 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tr_TR.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tr_TR.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Bu sunucuya zaten bağlısın\! velocity.error.already-connected-proxy=Bu sunucuya zaten bağlısın\! velocity.error.already-connecting=Bu sunucuya zaten bağlanmaya çalışıyorsun\! -velocity.error.cant-connect={0}\:{1} ile bağlantı kurulamadı -velocity.error.connecting-server-error=Seni {0}'ye bağlayamadık. Lütfen daha sonra tekrar deneyiniz. -velocity.error.connected-server-error={0} ile bağlantında bir problem meydana geldi. +velocity.error.cant-connect=\: ile bağlantı kurulamadı +velocity.error.connecting-server-error=Seni 'ye bağlayamadık. Lütfen daha sonra tekrar deneyiniz. +velocity.error.connected-server-error= ile bağlantında bir problem meydana geldi. velocity.error.internal-server-connection-error=Dahili sunucu bağlantısında bir hata meydana geldi. velocity.error.logging-in-too-fast=Çok hızlı bağlanmaya çalışıyorsun, daha sonra tekrar dene. velocity.error.online-mode-only=Minecraft hesabına bağlı değilsin. Eğer Minecraft hesabına bağlıysan, Minecraft istemcini kapatıp tekrardan başlat. velocity.error.player-connection-error=Bağlantında dahili bir hata meydana geldi. velocity.error.modern-forwarding-needs-new-client=Bu sunucuya Minecraft'ın sadece 1.13 ve üzeri sürümleri ile giriş yapabilirsin. velocity.error.modern-forwarding-failed=Sunucun Velocity'ye yönlendirme isteğinde bulunmadı. Sunucunun Velocity'nin ayarlarında ayarlandığına emin ol. -velocity.error.moved-to-new-server={0}\:{1} sunucusundan atıldınız +velocity.error.moved-to-new-server=\: sunucusundan atıldınız velocity.error.no-available-servers=Seni bağlayabileceğimiz bir sunucu bulamadık. Lütfen daha sonra tekrar dene veya bir yetkili ile iletişime geç. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=Bu komutu çalıştırırken bir hata meydana geldi. velocity.command.command-does-not-exist=Böyle bir komut yok. velocity.command.players-only=Sadece oyuncular bu komutu çalıştırabilir. -velocity.command.server-does-not-exist=Belirlenmiş sunucu {0} bulunmuyor. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Şu anda {0} sunucusuna bağlısın. +velocity.command.server-does-not-exist=Belirlenmiş sunucu bulunmuyor. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Şu anda sunucusuna bağlısın. velocity.command.server-too-many=Ayarlanmış birçok sunucu mevcut. Mevcut bütün sunucuları görmek için tamamlama özelliğini kullan. velocity.command.server-available=Müsait sunucular\: -velocity.command.server-tooltip-player-online={0} oyuncu çevrimiçi -velocity.command.server-tooltip-players-online={0} oyuncu çevrimiçi +velocity.command.server-tooltip-player-online= oyuncu çevrimiçi +velocity.command.server-tooltip-players-online= oyuncu çevrimiçi velocity.command.server-tooltip-current-server=Şu anda bu sunucuya bağlısın velocity.command.server-tooltip-offer-connect-server=Bu sunucuya bağlanmak için tıkla -velocity.command.glist-player-singular=Şu anda sunucuya toplam {0} oyuncu bağlı. -velocity.command.glist-player-plural=Şu anda sunucuya toplam {0} oyuncu bağlı. +velocity.command.glist-player-singular=Şu anda sunucuya toplam oyuncu bağlı. +velocity.command.glist-player-plural=Şu anda sunucuya toplam oyuncu bağlı. velocity.command.glist-view-all=Sunucudaki bütün oyuncuları görüntülemek için /glist all komutunu kullan. velocity.command.reload-success=Velocity ayarları başarıyla güncellendi. velocity.command.reload-failure=Velocity ayarlarınız güncellenemiyor. Daha fazla bilgi için konsolu kontrol edin. -velocity.command.version-copyright=Talif hakkı 2018-{2} {0}. {1}, GNU General Public License v3 adı altında lisanslanmıştır. +velocity.command.version-copyright=Talif hakkı 2018- . , GNU General Public License v3 adı altında lisanslanmıştır. velocity.command.no-plugins=Yüklenmiş herhangi bir eklenti yok. -velocity.command.plugins-list=Eklentiler\: {0} -velocity.command.plugin-tooltip-website=Website\: {0} -velocity.command.plugin-tooltip-author=Yapımcı\: {0} -velocity.command.plugin-tooltip-authors=Yapımcılar\: {0} +velocity.command.plugins-list=Eklentiler\: +velocity.command.plugin-tooltip-website=Website\: +velocity.command.plugin-tooltip-author=Yapımcı\: +velocity.command.plugin-tooltip-authors=Yapımcılar\: velocity.command.dump-uploading=Toplanılan bilgiler yükleniyor... velocity.command.dump-send-error=Velocity sunucuları ile iletişim sırasında bir hata oluştu. Sunucular geçici olarak kullanılamıyor olabilir veya ağ ayarlarınızla ilgili bir sorun olabilir. Velocity sunucunuzun logunda veya konsolunda daha fazla bilgi bulabilirsiniz. velocity.command.dump-success=Bu proxy hakkında faydalı bilgiler içeren anonim bir rapor oluşturdu. Bir geliştirici talep ettiyse, aşağıdaki bağlantıyı onlarla paylaşabilirsiniz\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_uk_UA.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_uk_UA.properties index 2400bd29..66d82c3d 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_uk_UA.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_uk_UA.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Ви вже підключені до цього серверу\! velocity.error.already-connected-proxy=Ви вже приєднані до даного проксі\! velocity.error.already-connecting=Ви вже приєднуєтесь до сервера\! -velocity.error.cant-connect=Не вдалося приєднатись до сервера {0}\: {1} -velocity.error.connecting-server-error=Не вдалося приєднати вас до сервера {0}. Будь ласка, спробуйте пізніше. -velocity.error.connected-server-error=З вашим приєднанням до сервера {0} виникла проблема. +velocity.error.cant-connect=Не вдалося приєднатись до сервера \: +velocity.error.connecting-server-error=Не вдалося приєднати вас до сервера . Будь ласка, спробуйте пізніше. +velocity.error.connected-server-error=З вашим приєднанням до сервера виникла проблема. velocity.error.internal-server-connection-error=На сервері виникла внутрішня помилка приєднання. velocity.error.logging-in-too-fast=Ви приєднуєтесь дуже швидко. Будь ласка, спробуйте пізніше. velocity.error.online-mode-only=Ви не увійшли у свій профіль Minecraft. Якщо ви впевнені, що увійшли у профіль, спробуйте перезапустити свій клієнт Minecraft. velocity.error.player-connection-error=У вашому підключенні виникла внутрішня помилка. velocity.error.modern-forwarding-needs-new-client=Даний сервер сумісний з версією Minecraft 1.13 і вище. velocity.error.modern-forwarding-failed=Ваш сервер не давав запит на переадресацію на проксі-сервер. Упевніться, що сервер налаштований на переадресацію Velocity. -velocity.error.moved-to-new-server=Ви кікнуті з сервера {0}\: {1} +velocity.error.moved-to-new-server=Ви кікнуті з сервера \: velocity.error.no-available-servers=Нема серверів, доступних до приєднання. Спробуйте пізніше чи зв'яжіться з адміністратором. velocity.error.illegal-chat-characters=Заборонені символи у чаті # Commands velocity.command.generic-error=Під час виконання даної команди виникла помилка. velocity.command.command-does-not-exist=Цієї команди не існує. velocity.command.players-only=Тільки гравці можуть використовувати цю команду. -velocity.command.server-does-not-exist=Вказаного серверу {0} не існує. -velocity.command.player-not-found=Вказаного гравця {0} не існує. -velocity.command.server-current-server=Зараз ви приєднані до сервера {0}. +velocity.command.server-does-not-exist=Вказаного серверу не існує. +velocity.command.player-not-found=Вказаного гравця не існує. +velocity.command.server-current-server=Зараз ви приєднані до сервера . velocity.command.server-too-many=Налаштовано дуже багато серверів. Для перегляду всіх доступних серверів, використовуйте автозаповнення клавішею Tab. velocity.command.server-available=Доступні сервери\: -velocity.command.server-tooltip-player-online={0} гравець онлайн -velocity.command.server-tooltip-players-online={0} гравець(ця, ців) онлайн +velocity.command.server-tooltip-player-online= гравець онлайн +velocity.command.server-tooltip-players-online= гравець(ця, ців) онлайн velocity.command.server-tooltip-current-server=Приєднаний до даного серверу velocity.command.server-tooltip-offer-connect-server=Натисніть, щоб приєднатись до даного серверу -velocity.command.glist-player-singular={0} Гравець приєднаний до проксі на даний момент. -velocity.command.glist-player-plural={0} гравець(ця, ців) приєднані до проксі на даний момент. +velocity.command.glist-player-singular= Гравець приєднаний до проксі на даний момент. +velocity.command.glist-player-plural= гравець(ця, ців) приєднані до проксі на даний момент. velocity.command.glist-view-all=Щоб переглянути всіх гравців на серверах, використовуйте /glist all. velocity.command.reload-success=Конфігурація Velocity успішно перезавантажена. velocity.command.reload-failure=Не вдалося перезантажити конфігурацію Velocity. Перевірте консоль для подробиць. -velocity.command.version-copyright=Авторське право 2018-{2} {0}. {1} ліцензовано на умовах GNU General Public License v3. +velocity.command.version-copyright=Авторське право 2018- . ліцензовано на умовах GNU General Public License v3. velocity.command.no-plugins=Плагіни відсутні. -velocity.command.plugins-list=Плагіни\: {0} -velocity.command.plugin-tooltip-website=Веб-сайт\: {0} -velocity.command.plugin-tooltip-author=Автор\: {0} -velocity.command.plugin-tooltip-authors=Автори\: {0} +velocity.command.plugins-list=Плагіни\: +velocity.command.plugin-tooltip-website=Веб-сайт\: +velocity.command.plugin-tooltip-author=Автор\: +velocity.command.plugin-tooltip-authors=Автори\: velocity.command.dump-uploading=Завантаження отриманої інформації... velocity.command.dump-send-error=Під час зв'язку з серверами Velocity виникла помилка. Можливо, сервери тимчасово недоступні або проблема з налаштування ми вашої мережі. Більше інформації ви можете знайти в журнвлі або консолі вашого серверу Velocity. velocity.command.dump-success=Створено анонімний звіт, що містить корисну інформацію про проксі. Якщо цей звіт запросив розробник, ви можете поділитися з ним наступним посиланням\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_vi_VN.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_vi_VN.properties index 5450e21e..8c31a09d 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_vi_VN.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_vi_VN.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=Bạn đã kết nối đến máy chủ này\! velocity.error.already-connected-proxy=Bạn đã kết nối đến proxy này\! velocity.error.already-connecting=Bạn đang kết nối đến một máy chủ\! -velocity.error.cant-connect=Không thể kết nối đến {0}\: {1} -velocity.error.connecting-server-error=Không thể kết nối bạn đến {0}. Vui lòng thử lại sau. -velocity.error.connected-server-error=Kết nối của bạn đến {0} đã gặp một vấn đề. +velocity.error.cant-connect=Không thể kết nối đến \: +velocity.error.connecting-server-error=Không thể kết nối bạn đến . Vui lòng thử lại sau. +velocity.error.connected-server-error=Kết nối của bạn đến đã gặp một vấn đề. velocity.error.internal-server-connection-error=Đã xảy ra một lỗi kết nối máy chủ nội bộ. velocity.error.logging-in-too-fast=Bạn đang đăng nhập quá nhanh, vui lòng thử lại sau. velocity.error.online-mode-only=Bạn chưa đăng nhập vào tài khoản Minecraft của bạn. Nếu bạn đã đăng nhập vào tài khoản Minecraft của bạn, hãy thử khởi động lại Minecraft. velocity.error.player-connection-error=Đã xảy ra một lỗi nội bộ trong kết nối của bạn. velocity.error.modern-forwarding-needs-new-client=Máy chủ này chỉ tương thích với phiên bản Minecraft 1.13 và trở lên. velocity.error.modern-forwarding-failed=Máy chủ của bạn đã không gửi yêu cầu chuyển tiếp đến proxy. Hãy bảo đảm máy chủ đã được điều chỉnh cho chuyển tiếp đến Velocity. -velocity.error.moved-to-new-server=Bạn đã bị đá khỏi {0}\: {1} +velocity.error.moved-to-new-server=Bạn đã bị đá khỏi \: velocity.error.no-available-servers=Không có máy chủ nào cho bạn để kết nối. Vui lòng thử lại sau hoặc liên lạc một quản trị viên. velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=Đã có lỗi xảy ra khi thực hiện lệnh này. velocity.command.command-does-not-exist=Lệnh này không tồn tại. velocity.command.players-only=Chỉ có người chơi có thể thực hiện lệnh này. -velocity.command.server-does-not-exist=Máy chủ chỉ định {0} không tồn tại. -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=Hiện bạn đang kết nối đến {0}. +velocity.command.server-does-not-exist=Máy chủ chỉ định không tồn tại. +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=Hiện bạn đang kết nối đến . velocity.command.server-too-many=Có quá nhiều máy chủ được cài đặt. Dùng tab để xem hết tất cả máy chủ đang có. velocity.command.server-available=Máy chủ có sẵn\: -velocity.command.server-tooltip-player-online={0} người chơi online -velocity.command.server-tooltip-players-online={0} người chơi online +velocity.command.server-tooltip-player-online= người chơi online +velocity.command.server-tooltip-players-online= người chơi online velocity.command.server-tooltip-current-server=Hiện tại đang kết nối đến máy chủ này velocity.command.server-tooltip-offer-connect-server=Click để kết nối đến máy chủ này -velocity.command.glist-player-singular={0} người chơi hiện đang kết nối đến proxy. -velocity.command.glist-player-plural={0} người chơi hiện đang kết nối đến proxy. +velocity.command.glist-player-singular= người chơi hiện đang kết nối đến proxy. +velocity.command.glist-player-plural= người chơi hiện đang kết nối đến proxy. velocity.command.glist-view-all=Để xem tất cả người chơi trên toàn bộ máy chủ, dùng /glist all. velocity.command.reload-success=Điều chỉnh Velocity đã tải lại thành công. velocity.command.reload-failure=Không thể tải lại điều chỉnh Velocity. Kiểm tra console để xem thêm chi tiết. -velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018- . is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=Hiện tại không có plugin được cài đặt. -velocity.command.plugins-list=Plugins\: {0} -velocity.command.plugin-tooltip-website=Website\: {0} -velocity.command.plugin-tooltip-author=Tác giả\: {0} -velocity.command.plugin-tooltip-authors=Các tác giả\: {0} +velocity.command.plugins-list=Plugins\: +velocity.command.plugin-tooltip-website=Website\: +velocity.command.plugin-tooltip-author=Tác giả\: +velocity.command.plugin-tooltip-authors=Các tác giả\: velocity.command.dump-uploading=Đang tải lên thông tin đã thu thập... velocity.command.dump-send-error=Đã xảy ra lỗi khi giao tiếp với máy chủ Velocity. Các máy chủ có thể tạm thời không khả dụng hoặc có sự cố với cài đặt mạng của bạn. Bạn có thể tìm thêm thông tin trong nhật ký hoặc bảng điều khiển của máy chủ Velocity của mình. velocity.command.dump-success=Đã tạo một báo cáo ẩn danh chứa thông tin hữu ích về proxy này. Nếu một nhà phát triển yêu cầu nó, bạn có thể chia sẻ liên kết sau với họ\: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_CN.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_CN.properties index 4b3b07d3..f56a89bc 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_CN.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_CN.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=您已经连接到此服务器了! velocity.error.already-connected-proxy=您已经连接到此代理服务器了! velocity.error.already-connecting=您已经在尝试连接服务器了! -velocity.error.cant-connect=无法连接至 {0}:{1} -velocity.error.connecting-server-error=无法将您连接至 {0},请稍后再试。 -velocity.error.connected-server-error=您与 {0} 的连接出现问题。 +velocity.error.cant-connect=无法连接至 +velocity.error.connecting-server-error=无法将您连接至 ,请稍后再试。 +velocity.error.connected-server-error=您与 的连接出现问题。 velocity.error.internal-server-connection-error=发送内部服务器连接错误。 velocity.error.logging-in-too-fast=您的登录过于频繁,请稍后重试。 velocity.error.online-mode-only=您尚未登录至 Minecraft 账户。若您已登录,请尝试重启您的客户端。 velocity.error.player-connection-error=您的连接发生内部错误。 velocity.error.modern-forwarding-needs-new-client=此服务器仅兼容 Minecraft 1.13 及更高版本。 velocity.error.modern-forwarding-failed=您的服务器未向代理服务器转发请求,请确保已配置 Velocity 转发。 -velocity.error.moved-to-new-server=您已被踢出 {0}:{1} +velocity.error.moved-to-new-server=您已被踢出 velocity.error.no-available-servers=您当前没有可连接的服务器,请稍后重试或联系管理员。 velocity.error.illegal-chat-characters=聊天中出现非法字符 # Commands velocity.command.generic-error=执行此命令时发生错误。 velocity.command.command-does-not-exist=此命令不存在。 velocity.command.players-only=只有玩家才能执行此命令。 -velocity.command.server-does-not-exist=指定的服务器 {0} 不存在。 -velocity.command.player-not-found=指定的玩家 {0} 不存在。 -velocity.command.server-current-server=您已连接至 {0}。 +velocity.command.server-does-not-exist=指定的服务器 不存在。 +velocity.command.player-not-found=指定的玩家 不存在。 +velocity.command.server-current-server=您已连接至 。 velocity.command.server-too-many=设置的服务器过多,请使用 Tab 键补全来查看所有可用的服务器。 velocity.command.server-available=可用的服务器: -velocity.command.server-tooltip-player-online={0} 名玩家在线 -velocity.command.server-tooltip-players-online={0} 名玩家在线 +velocity.command.server-tooltip-player-online= 名玩家在线 +velocity.command.server-tooltip-players-online= 名玩家在线 velocity.command.server-tooltip-current-server=当前已连接至此服务器 velocity.command.server-tooltip-offer-connect-server=点击连接至此服务器 -velocity.command.glist-player-singular=共有 {0} 名玩家已连接至此代理服务器。 -velocity.command.glist-player-plural=共有 {0} 名玩家已连接至此代理服务器。 +velocity.command.glist-player-singular=共有 名玩家已连接至此代理服务器。 +velocity.command.glist-player-plural=共有 名玩家已连接至此代理服务器。 velocity.command.glist-view-all=使用 /glist all 命令来查看所有服务器的全部玩家列表。 velocity.command.reload-success=Velocity 配置已成功重载。 velocity.command.reload-failure=无法重载 Velocity 配置,请查看控制台了解详情。 -velocity.command.version-copyright=Copyright 2018-{2} {0}。{1} 以 GNU 通用公共许可证第三版授权。 +velocity.command.version-copyright=Copyright 2018- 以 GNU 通用公共许可证第三版授权。 velocity.command.no-plugins=当前没有安装任何插件。 -velocity.command.plugins-list=插件:{0} -velocity.command.plugin-tooltip-website=网站:{0} -velocity.command.plugin-tooltip-author=作者:{0} -velocity.command.plugin-tooltip-authors=作者:{0} +velocity.command.plugins-list=插件: +velocity.command.plugin-tooltip-website=网站: +velocity.command.plugin-tooltip-author=作者: +velocity.command.plugin-tooltip-authors=作者: velocity.command.dump-uploading=正在上传已收集的信息... velocity.command.dump-send-error=与 Velocity 服务器通信时发生错误,服务器可能暂不可用或您的网络设置存在问题。您可在 Velocity 服务器日志或控制台中了解详情。 velocity.command.dump-success=已创建关于此代理的匿名反馈数据。若开发者需要,您可与其分享以下链接: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_HK.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_HK.properties index 37468347..f15eff97 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_HK.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_HK.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=你已經連接到這個伺服器\! velocity.error.already-connected-proxy=您已經連接到此代理伺服器了! velocity.error.already-connecting=您已經在嘗試連接伺服器了! -velocity.error.cant-connect=無法法連接至 {0}:{1} -velocity.error.connecting-server-error=無法將您連接至 {0},請稍后再試。 -velocity.error.connected-server-error=你與 {0} 的連線出現問題 +velocity.error.cant-connect=無法法連接至 +velocity.error.connecting-server-error=無法將您連接至 ,請稍后再試。 +velocity.error.connected-server-error=你與 的連線出現問題 velocity.error.internal-server-connection-error=出現內部伺服器連線問題 velocity.error.logging-in-too-fast=你登錄太快,請稍後再試 velocity.error.online-mode-only=你沒有登入你的Minecraft賬號。如果你已經登入,請嘗試重啟你的Minecraft用戶端。 velocity.error.player-connection-error=你的連線發生內部錯誤 velocity.error.modern-forwarding-needs-new-client=此伺服器只兼容Minecraft版本1.13以上 velocity.error.modern-forwarding-failed=你的伺服器未向代理伺服器發送轉發請求,請確保你的伺服器已配置Velocity轉發 -velocity.error.moved-to-new-server=你已被踢出{0}\:{1} +velocity.error.moved-to-new-server=你已被踢出\: velocity.error.no-available-servers=當前沒有可以連接你的伺服器,請稍後再試或聯繫管理員 velocity.error.illegal-chat-characters=Illegal characters in chat # Commands velocity.command.generic-error=執行命令時發生錯誤 velocity.command.command-does-not-exist=此命令不存在 velocity.command.players-only=只有玩家能執行此命令 -velocity.command.server-does-not-exist=指定的伺服器 {0} 不存在 -velocity.command.player-not-found=The specified player {0} does not exist. -velocity.command.server-current-server=您已連接至 {0}。 +velocity.command.server-does-not-exist=指定的伺服器 不存在 +velocity.command.player-not-found=The specified player does not exist. +velocity.command.server-current-server=您已連接至 。 velocity.command.server-too-many=設置的伺服器國多,請使用 Tab 鍵補全以查看所有可用的服务器。 velocity.command.server-available=可用的伺服器: -velocity.command.server-tooltip-player-online={0} 個玩家在線 -velocity.command.server-tooltip-players-online={0} 個玩家在線 +velocity.command.server-tooltip-player-online= 個玩家在線 +velocity.command.server-tooltip-players-online= 個玩家在線 velocity.command.server-tooltip-current-server=當前已連接至此伺服器 velocity.command.server-tooltip-offer-connect-server=點擊連接至此伺服器 -velocity.command.glist-player-singular=共有 {0} 個玩家已連接至此代理伺服器。 -velocity.command.glist-player-plural=共有 {0} 個玩家已連接至此代理伺服器。 +velocity.command.glist-player-singular=共有 個玩家已連接至此代理伺服器。 +velocity.command.glist-player-plural=共有 個玩家已連接至此代理伺服器。 velocity.command.glist-view-all=使用 /glist all 命令來查看所有伺服器的玩家列表。 velocity.command.reload-success=Velocity 配置重新加載完成。 velocity.command.reload-failure=無法重新加載 Velocity 配置,請查看控制台了解詳情。 -velocity.command.version-copyright=Copyright 2018-{2} {0} ( {1} 的授權條款爲: GNU 通用公共授權條款第三版) +velocity.command.version-copyright=Copyright 2018- 的授權條款爲: GNU 通用公共授權條款第三版) velocity.command.no-plugins=目前未有安裝任何 Velocity 插件。 -velocity.command.plugins-list=插件: {0} -velocity.command.plugin-tooltip-website=網站: {0} -velocity.command.plugin-tooltip-author=作者: {0} -velocity.command.plugin-tooltip-authors=作者: {0} +velocity.command.plugins-list=插件: +velocity.command.plugin-tooltip-website=網站: +velocity.command.plugin-tooltip-author=作者: +velocity.command.plugin-tooltip-authors=作者: velocity.command.dump-uploading=正在收集並上載 Velocity 設定資料... velocity.command.dump-send-error=與 Velocity 伺服器通信時發生錯誤,伺服器可能暫時不可用或您的網路設置存在問題。您可在 Velocity 伺服器日志或控制台中了解詳情。 velocity.command.dump-success=已創建關於此代理的匿名反饋數據。若開發人員需要,您可與其分享以下鏈接: diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_TW.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_TW.properties index b7fe1b5b..8b0ff331 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_TW.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_TW.properties @@ -18,42 +18,42 @@ velocity.error.already-connected=您已經連線到此伺服器了! velocity.error.already-connected-proxy=您已經連線到此代理伺服器了! velocity.error.already-connecting=您已經在嘗試連線伺服器了! -velocity.error.cant-connect=無法連線到 {0}:{1} -velocity.error.connecting-server-error=無法將您連線至 {0},請稍後再試。 -velocity.error.connected-server-error=您與 {0} 的連線出現問題。 +velocity.error.cant-connect=無法連線到 +velocity.error.connecting-server-error=無法將您連線至 ,請稍後再試。 +velocity.error.connected-server-error=您與 的連線出現問題。 velocity.error.internal-server-connection-error=發送內部伺服器連線錯誤。 velocity.error.logging-in-too-fast=您的登入過於頻繁,請稍後再試。 velocity.error.online-mode-only=您尚未登入至 Minecraft 帳號。若您已登入,請嘗試重啟您的啟動器。 velocity.error.player-connection-error=您的連線發生內部錯誤。 velocity.error.modern-forwarding-needs-new-client=此伺服器僅相容 Minecraft 1.13 及更高版本。 velocity.error.modern-forwarding-failed=您的伺服器未向代理伺服器轉發請求,請確保已配置 Velocity 轉發。 -velocity.error.moved-to-new-server=您已被踢出 {0}:{1} +velocity.error.moved-to-new-server=您已被踢出 velocity.error.no-available-servers=您當前沒有可連線的伺服器,請稍後重試或聯絡管理員。 velocity.error.illegal-chat-characters=聊天欄中出現不允許的字符 # Commands velocity.command.generic-error=執行此指令時發生錯誤。 velocity.command.command-does-not-exist=此指令不存在。 velocity.command.players-only=只有玩家才能執行這個指令。 -velocity.command.server-does-not-exist=指定的伺服器 {0} 不存在。 -velocity.command.player-not-found=指定的玩家 {0} 不存在。 -velocity.command.server-current-server=您已連線到 {0}。 +velocity.command.server-does-not-exist=指定的伺服器 不存在。 +velocity.command.player-not-found=指定的玩家 不存在。 +velocity.command.server-current-server=您已連線到 。 velocity.command.server-too-many=設定的伺服器過多,請使用 Tab 鍵補全來查看所有可用的伺服器。 velocity.command.server-available=可用的伺服器: -velocity.command.server-tooltip-player-online={0} 名玩家在線 -velocity.command.server-tooltip-players-online={0} 名玩家在線 +velocity.command.server-tooltip-player-online= 名玩家在線 +velocity.command.server-tooltip-players-online= 名玩家在線 velocity.command.server-tooltip-current-server=當前已連線至此伺服器 velocity.command.server-tooltip-offer-connect-server=點擊連線至此伺服器 -velocity.command.glist-player-singular=共有 {0} 名玩家已連線至此代理伺服器。 -velocity.command.glist-player-plural=共有 {0} 名玩家已連線至此代理伺服器。 +velocity.command.glist-player-singular=共有 名玩家已連線至此代理伺服器。 +velocity.command.glist-player-plural=共有 名玩家已連線至此代理伺服器。 velocity.command.glist-view-all=使用 /glist all 命令來查看所有伺服器的全部玩家列表。 velocity.command.reload-success=Velocity 配置已成功重載。 velocity.command.reload-failure=無法重載 Velocity 配置,請查看控制台了解詳情。 -velocity.command.version-copyright=Copyright 2018-{2} {0}。{1} 以 GNU 通用公共許可證第三版授權。 +velocity.command.version-copyright=Copyright 2018- 以 GNU 通用公共許可證第三版授權。 velocity.command.no-plugins=當前沒有安裝任何插件。 -velocity.command.plugins-list=插件:{0} -velocity.command.plugin-tooltip-website=網站:{0} -velocity.command.plugin-tooltip-author=作者:{0} -velocity.command.plugin-tooltip-authors=作者:{0} +velocity.command.plugins-list=插件: +velocity.command.plugin-tooltip-website=網站: +velocity.command.plugin-tooltip-author=作者: +velocity.command.plugin-tooltip-authors=作者: velocity.command.dump-uploading=正在上傳已收集的訊息... velocity.command.dump-send-error=與 Velocity 伺服器通訊時發生錯誤,伺服器可能暫不可用或您的網路設置存在問題。您可在 Velocity 伺服器日誌或控制台中了解詳情。 velocity.command.dump-success=已創建關於此代理的匿名反饋資料。若開發者需要,您可與其分享以下連結: diff --git a/proxy/src/main/resources/default-velocity.toml b/proxy/src/main/resources/default-velocity.toml index 4d71e589..6aa5ceaa 100644 --- a/proxy/src/main/resources/default-velocity.toml +++ b/proxy/src/main/resources/default-velocity.toml @@ -74,6 +74,11 @@ sample-players-in-ping = false # If not enabled (default is true) player IP addresses will be replaced by in logs enable-player-address-logging = true +[packet-limiter] +interval = 7 +packets-per-second = 500 +bytes-per-second = -1 + [servers] # Configure your servers here. Each key represents the server's name, and the value # represents the IP address of the server to connect to. diff --git a/proxy/src/test/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNodeTests.java b/proxy/src/test/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNodeTests.java index fb5f57e3..90420da0 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNodeTests.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNodeTests.java @@ -64,8 +64,8 @@ public class VelocityArgumentCommandNodeTests { assertFalse(reader.canRead()); assertFalse(this.contextBuilder.getNodes().isEmpty()); - assertSame(node, this.contextBuilder.getNodes().get(0).getNode()); - assertEquals(expectedRange, this.contextBuilder.getNodes().get(0).getRange()); + assertSame(node, this.contextBuilder.getNodes().getFirst().getNode()); + assertEquals(expectedRange, this.contextBuilder.getNodes().getFirst().getRange()); assertTrue(this.contextBuilder.getArguments().containsKey("foo")); final ParsedArgument parsed = diff --git a/proxy/src/test/java/com/velocitypowered/proxy/scheduler/DeterministicSchedulerBackend.java b/proxy/src/test/java/com/velocitypowered/proxy/scheduler/DeterministicSchedulerBackend.java new file mode 100644 index 00000000..8a472ceb --- /dev/null +++ b/proxy/src/test/java/com/velocitypowered/proxy/scheduler/DeterministicSchedulerBackend.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2018-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.scheduler; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.time.Duration; +import java.util.PriorityQueue; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Delayed; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * A deterministic {@link SchedulerBackend} for tests. + * + *

This backend does not use the wall clock. Tests manually advance time, and all due tasks + * are executed deterministically on the calling thread. + */ +class DeterministicSchedulerBackend implements SchedulerBackend { + + private final Object lock = new Object(); + private final PriorityQueue queue = new PriorityQueue<>(); + private boolean shutdown; + private long nowNanos; + private long seq; + + @Override + public ScheduledFuture schedule(Runnable task, long delay, TimeUnit unit) { + checkNotNull(task, "task"); + checkNotNull(unit, "unit"); + return enqueue(task, unit.toNanos(delay), 0); + } + + @Override + public ScheduledFuture scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) { + checkNotNull(task, "task"); + checkNotNull(unit, "unit"); + checkArgument(period > 0, "period must be > 0"); + return enqueue(task, unit.toNanos(initialDelay), unit.toNanos(period)); + } + + @Override + public void shutdown() { + synchronized (lock) { + shutdown = true; + queue.clear(); + } + } + + /** + * Runs all tasks that are due "now" without advancing time. + */ + void runUntilIdle() { + drainDueTasks(); + } + + /** + * Advances virtual time and runs all tasks that become due. + * + * @param duration the amount of time to advance + */ + void advance(Duration duration) { + checkNotNull(duration, "duration"); + advance(duration.toNanos()); + } + + /** + * Advances virtual time and runs all tasks that become due. + * + * @param time the time to advance + * @param unit the unit + */ + void advance(long time, TimeUnit unit) { + checkNotNull(unit, "unit"); + advance(unit.toNanos(time)); + } + + private void advance(long nanos) { + if (nanos < 0) { + throw new IllegalArgumentException("nanos must be >= 0"); + } + synchronized (lock) { + nowNanos += nanos; + } + drainDueTasks(); + } + + private ScheduledFuture enqueue(Runnable task, long delayNanos, long periodNanos) { + synchronized (lock) { + if (shutdown) { + throw new java.util.concurrent.RejectedExecutionException("backend is shut down"); + } + Entry entry = new Entry(task, nowNanos + Math.max(0, delayNanos), periodNanos, seq++); + entry.future = new FutureImpl(entry); + queue.add(entry); + return entry.future; + } + } + + private void drainDueTasks() { + while (true) { + Entry entry; + synchronized (lock) { + entry = queue.peek(); + if (entry == null || entry.nextRunNanos > nowNanos) { + return; + } + queue.poll(); + } + + // Run outside the lock to avoid deadlocks if tasks schedule more work. + if (!entry.future.isCancelled()) { + try { + entry.task.run(); + } finally { + // no-op + } + } + + synchronized (lock) { + if (entry.future.isCancelled()) { + // Cancelled tasks are not re-queued. + continue; + } + + if (entry.periodNanos == 0) { + entry.future.complete(); + } else { + // Fixed-rate semantics: next run time is based on the scheduled time, not completion time. + entry.nextRunNanos = entry.nextRunNanos + entry.periodNanos; + queue.add(entry); + } + } + } + } + + private final class FutureImpl implements ScheduledFuture { + + private final Entry entry; + private final CountDownLatch completion = new CountDownLatch(1); + private volatile boolean cancelled; + private volatile boolean done; + + private FutureImpl(Entry entry) { + this.entry = entry; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + synchronized (lock) { + if (done) { + return false; + } + cancelled = true; + done = true; + queue.remove(entry); + } + completion.countDown(); + return true; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public boolean isDone() { + return done; + } + + void complete() { + if (!done) { + done = true; + completion.countDown(); + } + } + + @Override + public Object get() throws InterruptedException { + completion.await(); + if (cancelled) { + throw new CancellationException(); + } + return null; + } + + @Override + public Object get(long timeout, TimeUnit unit) throws InterruptedException, java.util.concurrent.TimeoutException { + if (!completion.await(timeout, unit)) { + throw new java.util.concurrent.TimeoutException(); + } + if (cancelled) { + throw new CancellationException(); + } + return null; + } + + @Override + public long getDelay(TimeUnit unit) { + synchronized (lock) { + long remaining = Math.max(0, entry.nextRunNanos - nowNanos); + return unit.convert(remaining, TimeUnit.NANOSECONDS); + } + } + + @Override + public int compareTo(Delayed o) { + long d1 = getDelay(TimeUnit.NANOSECONDS); + long d2 = o.getDelay(TimeUnit.NANOSECONDS); + return Long.compare(d1, d2); + } + } + + private static final class Entry implements Comparable { + + private final Runnable task; + private final long periodNanos; + private final long sequence; + private long nextRunNanos; + private FutureImpl future; + + private Entry(Runnable task, long nextRunNanos, long periodNanos, long sequence) { + this.task = task; + this.nextRunNanos = nextRunNanos; + this.periodNanos = periodNanos; + this.sequence = sequence; + } + + @Override + public int compareTo(Entry other) { + int cmp = Long.compare(this.nextRunNanos, other.nextRunNanos); + if (cmp != 0) { + return cmp; + } + return Long.compare(this.sequence, other.sequence); + } + } +} diff --git a/proxy/src/test/java/com/velocitypowered/proxy/scheduler/VelocitySchedulerTest.java b/proxy/src/test/java/com/velocitypowered/proxy/scheduler/VelocitySchedulerTest.java index e31a8a7e..15b8f1e9 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/scheduler/VelocitySchedulerTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/scheduler/VelocitySchedulerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2023 Velocity Contributors + * Copyright (C) 2018-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 @@ -18,6 +18,7 @@ package com.velocitypowered.proxy.scheduler; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.velocitypowered.api.scheduler.ScheduledTask; import com.velocitypowered.api.scheduler.TaskStatus; @@ -31,22 +32,26 @@ import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Test; class VelocitySchedulerTest { - // TODO: The timings here will be inaccurate on slow systems. @Test void buildTask() throws Exception { - VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager()); + DeterministicSchedulerBackend backend = new DeterministicSchedulerBackend(); + VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager(), backend); + CountDownLatch latch = new CountDownLatch(1); - ScheduledTask task = scheduler.buildTask(FakePluginManager.PLUGIN_A, latch::countDown) - .schedule(); - latch.await(); + ScheduledTask task = scheduler.buildTask(FakePluginManager.PLUGIN_A, latch::countDown).schedule(); + + backend.runUntilIdle(); // runs tasks due at t=0 + assertTrue(latch.await(5, TimeUnit.SECONDS)); + ((VelocityTask) task).awaitCompletion(); assertEquals(TaskStatus.FINISHED, task.status()); } @Test - void cancelWorks() throws Exception { - VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager()); + void cancelWorks() { + DeterministicSchedulerBackend backend = new DeterministicSchedulerBackend(); + VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager(), backend); AtomicInteger i = new AtomicInteger(3); ScheduledTask task = scheduler.buildTask(FakePluginManager.PLUGIN_A, i::decrementAndGet) .delay(100, TimeUnit.SECONDS) @@ -58,19 +63,26 @@ class VelocitySchedulerTest { @Test void repeatTaskWorks() throws Exception { - VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager()); + DeterministicSchedulerBackend backend = new DeterministicSchedulerBackend(); + VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager(), backend); + CountDownLatch latch = new CountDownLatch(3); ScheduledTask task = scheduler.buildTask(FakePluginManager.PLUGIN_A, latch::countDown) .delay(100, TimeUnit.MILLISECONDS) .repeat(100, TimeUnit.MILLISECONDS) .schedule(); - latch.await(); + + backend.advance(300, TimeUnit.MILLISECONDS); // triggers 3 timer firings deterministically + assertTrue(latch.await(5, TimeUnit.SECONDS)); + task.cancel(); } @Test void obtainTasksFromPlugin() throws Exception { - VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager()); + DeterministicSchedulerBackend backend = new DeterministicSchedulerBackend(); + VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager(), backend); + CountDownLatch runningLatch = new CountDownLatch(1); CountDownLatch endingLatch = new CountDownLatch(1); @@ -86,16 +98,19 @@ class VelocitySchedulerTest { .repeat(Duration.ofMillis(5)) .schedule(); - runningLatch.await(); + backend.advance(50, TimeUnit.MILLISECONDS); // run first tick only (no wall clock) + assertTrue(runningLatch.await(5, TimeUnit.SECONDS)); - assertEquals(scheduler.tasksByPlugin(FakePluginManager.PLUGIN_A).size(), 1); + assertEquals(1, scheduler.tasksByPlugin(FakePluginManager.PLUGIN_A).size()); endingLatch.countDown(); } @Test void testConsumerCancel() throws Exception { - VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager()); + DeterministicSchedulerBackend backend = new DeterministicSchedulerBackend(); + VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager(), backend); + CountDownLatch latch = new CountDownLatch(1); ScheduledTask task = scheduler.buildTask( @@ -108,14 +123,17 @@ class VelocitySchedulerTest { assertEquals(TaskStatus.SCHEDULED, task.status()); - latch.await(); + backend.runUntilIdle(); // initialDelay is 0 -> due immediately in virtual time + assertTrue(latch.await(5, TimeUnit.SECONDS)); assertEquals(TaskStatus.CANCELLED, task.status()); } @Test void testConsumerEquality() throws Exception { - VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager()); + DeterministicSchedulerBackend backend = new DeterministicSchedulerBackend(); + VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager(), backend); + CountDownLatch latch = new CountDownLatch(1); AtomicReference consumerTask = new AtomicReference<>(); @@ -127,10 +145,10 @@ class VelocitySchedulerTest { }).delay(60, TimeUnit.MILLISECONDS).schedule(); initialTask.set(task); - latch.await(); + + backend.advance(60, TimeUnit.MILLISECONDS); + assertTrue(latch.await(5, TimeUnit.SECONDS)); assertEquals(consumerTask.get(), initialTask.get()); - } - -} \ No newline at end of file +} diff --git a/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java b/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java index 7992ac52..4d1f66cc 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java @@ -62,16 +62,12 @@ public class FakePluginManager implements PluginManager { @Override public @NonNull Optional getPlugin(@NonNull String id) { - switch (id) { - case "a": - return Optional.of(containerA); - case "b": - return Optional.of(containerB); - case "velocity": - return Optional.of(containerVelocity); - default: - return Optional.empty(); - } + return switch (id) { + case "a" -> Optional.of(containerA); + case "b" -> Optional.of(containerB); + case "velocity" -> Optional.of(containerVelocity); + default -> Optional.empty(); + }; } @Override diff --git a/settings.gradle.kts b/settings.gradle.kts index 40384465..3159fc73 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,7 +17,7 @@ pluginManagement { } plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } rootProject.name = "velocity" @@ -36,8 +36,3 @@ sequenceOf( val deprecatedConfigurateModule = ":deprecated-configurate3" include(deprecatedConfigurateModule) project(deprecatedConfigurateModule).projectDir = file("proxy/deprecated/configurate3") - -// Log4J2 plugin -val log4j2ProxyPlugin = ":velocity-proxy-log4j2-plugin" -include(log4j2ProxyPlugin) -project(log4j2ProxyPlugin).projectDir = file("proxy/log4j2-plugin")