Compare commits

...

59 Commits

Author SHA1 Message Date
873fca763d Merge remote-tracking branch 'upstream/dev/3.0.0'
All checks were successful
SteamWarCI Build successful
2025-07-28 18:34:52 +02:00
e99407132f Add version information for 1.21.8 (#1612) 2025-07-18 03:26:24 +01:00
67d63faeca Reapply "Disable io_uring transport by default"
All checks were successful
SteamWarCI Build successful
This reverts commit 11834de220.
2025-07-10 10:42:11 +02:00
871b053561 Merge remote-tracking branch 'upstream/dev/3.0.0' 2025-07-10 10:41:50 +02:00
81deb1fff8 Update maven publishing repo name 2025-06-30 15:22:39 +01:00
59560ebad1 1.21.7 Support (#1598)
* Support 1.21.7 RC 1

* Use snapshot protocol for RC 1

* Support 1.21.7 RC 2

* Set release protocol for 1.21.7

* Update api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java

---------

Co-authored-by: Nassim Jahnke <nassim@njahnke.dev>
2025-06-30 16:09:39 +02:00
67a6600c05 New Crowdin updates (#1283)
* New translations messages.properties (Bulgarian)

* New translations messages.properties (Spanish)
2025-06-29 23:31:24 -07:00
f3e30558e4 Gradle deprecation fixes & upgrades (#1594)
* Fix Gradle deprecations

By using test suites, we explicitely configure the relevant dependencies
on the test sourceset. This is not done by merely configuring the test task.

* Switch to maintained version of Shadow

* Update to Gradle 8.14.2
2025-06-28 16:28:29 -07:00
e46ab6ad7d build: publish using fill (#1599) 2025-06-28 16:12:00 -07:00
b6fd48f282 Update to adventure 4.22.0 (#1595) 2025-06-27 16:56:05 +01:00
c2edc26d8e Merge pull request 'Update Velocity' (#2) from update into master
All checks were successful
SteamWarCI Build successful
Reviewed-on: #2
2025-06-26 23:11:02 +02:00
76417b13d4 Merge branch 'updatev2' into update
All checks were successful
SteamWarCI Build successful
2025-06-26 22:53:32 +02:00
47f36e3ff9 Merge remote-tracking branch 'upstream/dev/3.0.0' into updatev2
All checks were successful
SteamWarCI Build successful
2025-06-26 22:52:31 +02:00
10e75b6d55 feat: property support for max clientside channels (#1557) 2025-06-21 10:05:04 +02:00
fe69214e77 Downgrade netty (Fixes #1591) 2025-06-17 18:46:22 +01:00
020c7fe6f5 1.21.6 (#1580) 2025-06-17 14:52:50 +01:00
44bc15db40 Add system property to skip packet compression threshold validation 2025-06-14 20:29:12 +01:00
549b8d624e Update some dependencies (#1576) 2025-06-14 19:53:22 +01:00
669fda298c Only apply max known pack restrictions Serverbound 2025-06-11 10:12:48 +01:00
75bb48d00e Merge remote-tracking branch 'upstream/dev/3.0.0' into updatev2
All checks were successful
SteamWarCI Build successful
2025-06-03 23:12:58 +02:00
AR
21ecd344ba Fix HoverEvent.showEntity() in protocol versions prior to 1.21.5 (#1578) 2025-05-25 21:24:21 +01:00
8c8162dbf6 Discard known packs if we don't have a target 2025-05-24 18:28:54 +01:00
5e20ec19ff Stabilize and expose suggestions API (#1406)
* Expose suggestions API

* Improve javadoc of suggestions api
2025-05-23 14:23:38 +01:00
5eb83760cd Attempt to improve partial read situations during early connections 2025-05-22 14:26:48 +01:00
8fea43d6ba Add some means to quickly overlook deframing issues 2025-05-22 13:13:06 +01:00
678c7aa3a4 Modern-ify Adventure uses and fix bug in TranslatableMapper (#1575)
* fix: Don't ignore the player's locale in message translation

* feature: Use PointersSupplier to save constructing a Pointers instance for every player

* fix: Don't use a custom implementation of Identity for players

We don't need to carry about this object for every player.

* chore: Stop using deprecated TranslationRegistry

* fix: Simplify TranslatableMapper and fix bugs

- The fallback string is not intended to be translated, so don't do that.
- Check if the string can be translated in the default locale before using the closest mapper as devs may have their own strings.
- Remove the hardcoded check for TranslationRegistry instance as devs (and us now) can use non-TranslationRegistry translator instances.
2025-05-21 18:06:26 +01:00
e13c8c340f Increase limit to account for strings being strings 2025-05-09 22:17:04 +01:00
dc659538d3 Set netty allocator earlier and more globally 2025-05-09 20:19:29 +01:00
eb099d1220 Merge branches 'dev/3.0.0' and 'dev/3.0.0' of github.com:PaperMC/Velocity into dev/3.0.0 2025-05-09 15:55:40 +01:00
1ad1f3b215 Use pooled netty allocator instead of default adaptive allocator (#1570) 2025-05-08 11:30:55 +01:00
063065b21a fix: adventure 4.21.0 adaptation (#1569) 2025-05-08 09:55:16 +01:00
00016ba4e1 Validate handshake packet length early 2025-05-08 00:15:50 +01:00
91a61643bd Revert "Disable io_uring transport by default"
All checks were successful
SteamWarCI Build successful
This reverts commit ae312339a3.
2025-04-27 20:24:41 +02:00
b6e05cb0b9 Refactor TCP Fast Open checks and update message identifiers.
All checks were successful
SteamWarCI Build successful
Removed transport type conditions for TCP Fast Open to streamline configuration usage. Added imports for new message identifiers in `ClientPlaySessionHandler`. Cleaned up Netty library definitions in `libs.versions.toml`.
2025-04-27 20:09:05 +02:00
1507b91463 Merge remote-tracking branch 'upstream/dev/3.0.0' into update
# Conflicts:
#	proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java
2025-04-27 19:53:15 +02:00
b06af3718c Merge remote-tracking branch 'github/dev/3.0.0'
All checks were successful
SteamWarCI Build successful
# Conflicts:
#	proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerCommandPacket.java
2025-04-01 07:06:14 +02:00
a20a896582 Skip javadoc generation
All checks were successful
SteamWarCI Build successful
2025-01-22 09:37:30 +01:00
e1a3421212 Adapt to new server
Some checks failed
SteamWarCI Build failed
2025-01-22 09:33:59 +01:00
19e51a2b12 Merge remote-tracking branch 'upstream/dev/3.0.0' 2024-12-06 11:14:27 +01:00
b89a5c5ce9 Fix CI 2024-12-02 12:45:04 +01:00
65d3277319 Merge remote-tracking branch 'upstream/dev/3.0.0' 2024-11-30 09:25:25 +01:00
a22bfa10f9 Merge pull request 'Update 1.21.2 client support' (#5) from upstream into master
Reviewed-on: https://steamwar.de/devlabs/SteamWar/Velocity/pulls/5
2024-11-11 08:15:32 +01:00
d9d1319a3a Merge remote-tracking branch 'upstream/dev/3.0.0' into upstream
# Conflicts:
#	proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java
2024-11-10 18:32:48 +01:00
15ecbf4345 Merge pull request 'Update Velocity (might fix Command problems) and fix PluginMessages...' (#4) from fix-pluginmessages into master
Reviewed-on: https://steamwar.de/devlabs/SteamWar/Velocity/pulls/4
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2024-08-20 08:14:11 +02:00
5e3bbcd427 Fix command signature issues. 2024-08-20 08:03:18 +02:00
a6c79db07b Remove filter checks to receive PluginMessages unfiltered. 2024-08-18 15:32:08 +02:00
6e33bc6c17 Merge remote-tracking branch 'refs/remotes/upstream/dev/3.0.0' 2024-08-18 15:28:10 +02:00
01208bb359 Indicate NoChatReports support in ServerPing 2024-06-24 18:36:32 +02:00
fa88aaae52 Always unsign chat. 2024-06-21 12:48:58 +02:00
2da400a267 Merge pull request 'Implement Velocity PRs #998 #1246 and #1309 (io_uring, tcp_fastopen and PluginMessage race condition fix)' (#3) from io_uring into master
Reviewed-on: https://steamwar.de/devlabs/SteamWar/Velocity/pulls/3
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2024-06-19 12:06:10 +02:00
8103135dfb Fix type 2024-06-19 10:21:36 +02:00
cfabff7288 Implement Velocity PRs #998 #1246 and #1309 (io_uring, tcp_fastopen and PluginMessage race condition fix) 2024-06-19 10:11:32 +02:00
2f5a27a708 Fix CI 2024-06-19 09:47:18 +02:00
fdfe8bcc4b Fix CI 2024-06-19 09:40:58 +02:00
a19fd8db74 Add UpdateTeamsPacket 2024-06-16 21:27:13 +02:00
e63d71423d Add UpdateTeamsPacket 2024-06-16 21:24:06 +02:00
a7afe35fab Rebuild 2024-06-16 13:25:07 +02:00
56d6339313 Fix JVM 2024-06-16 13:18:29 +02:00
2475572573 Add steamwarci.yml 2024-06-16 12:52:43 +02:00
41 changed files with 648 additions and 211 deletions

View File

@ -14,10 +14,10 @@ jobs:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
java-version: 21
distribution: 'zulu'
- name: Build with Gradle
run: ./gradlew build

View File

@ -7,8 +7,10 @@
package com.velocitypowered.api.command;
import com.mojang.brigadier.suggestion.Suggestions;
import com.velocitypowered.api.event.command.CommandExecuteEvent;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -116,6 +118,27 @@ public interface CommandManager {
*/
CompletableFuture<Boolean> executeImmediatelyAsync(CommandSource source, String cmdLine);
/**
* Asynchronously collects suggestions to fill in the given command {@code cmdLine}.
* Returns only the raw completion suggestions without tooltips.
*
* @param source the source to execute the command for
* @param cmdLine the partially completed command
* @return a {@link CompletableFuture} eventually completed with a {@link List}, possibly empty
*/
CompletableFuture<List<String>> offerSuggestions(CommandSource source, String cmdLine);
/**
* Asynchronously collects suggestions to fill in the given command {@code cmdLine}.
* Returns the brigadier {@link Suggestions} with tooltips for each result.
*
* @param source the source to execute the command for
* @param cmdLine the partially completed command
* @return a {@link CompletableFuture} eventually completed with {@link Suggestions}, possibly
* empty
*/
CompletableFuture<Suggestions> offerBrigadierSuggestions(CommandSource source, String cmdLine);
/**
* Returns an immutable collection of the case-insensitive aliases registered
* on this manager.

View File

@ -90,7 +90,9 @@ public enum ProtocolVersion implements Ordered<ProtocolVersion> {
MINECRAFT_1_21(767, "1.21", "1.21.1"),
MINECRAFT_1_21_2(768, "1.21.2", "1.21.3"),
MINECRAFT_1_21_4(769, "1.21.4"),
MINECRAFT_1_21_5(770, "1.21.5");
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");
private static final int SNAPSHOT_BIT = 30;

View File

@ -31,6 +31,7 @@ public final class ServerPing {
private final net.kyori.adventure.text.Component description;
private final @Nullable Favicon favicon;
private final @Nullable ModInfo modinfo;
private final boolean preventsChatReports = true;
public ServerPing(Version version, @Nullable Players players,
net.kyori.adventure.text.Component description, @Nullable Favicon favicon) {

View File

@ -2,8 +2,15 @@ import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.withType
import java.io.ByteArrayOutputStream
// This interface is needed as a workaround to get an instance of ExecOperations
interface Injected {
@get:Inject
val execOps: ExecOperations
}
val currentShortRevision = ByteArrayOutputStream().use {
exec {
val execOps = objects.newInstance<Injected>().execOps
execOps.exec {
executable = "git"
args = listOf("rev-parse", "HEAD")
standardOutput = it

View File

@ -8,7 +8,7 @@ extensions.configure<PublishingExtension> {
maven {
credentials(PasswordCredentials::class.java)
name = "paper"
name = if (version.toString().endsWith("SNAPSHOT")) "paperSnapshots" else "paper" // "paper" is seemingly not defined
val base = "https://repo.papermc.io/repository/maven"
val releasesRepoUrl = "$base-releases/"
val snapshotsRepoUrl = "$base-snapshots/"

View File

@ -20,11 +20,11 @@ subprojects {
testImplementation(rootProject.libs.junit)
}
tasks {
test {
useJUnitPlatform()
reports {
junitXml.required.set(true)
testing.suites.named<JvmTestSuite>("test") {
useJUnitJupiter()
targets.all {
testTask.configure {
reports.junitXml.required = true
}
}
}

View File

@ -2,19 +2,20 @@
configurate3 = "3.7.3"
configurate4 = "4.1.2"
flare = "2.0.1"
log4j = "2.24.1"
netty = "4.2.0.Final"
log4j = "2.24.3"
netty = "4.2.1.Final"
[plugins]
fill = "io.papermc.fill.gradle:1.0.3"
indra-publishing = "net.kyori.indra.publishing:2.0.6"
shadow = "io.github.goooler.shadow:8.1.5"
shadow = "com.gradleup.shadow:8.3.6"
spotless = "com.diffplug.spotless:6.25.0"
[libraries]
adventure-bom = "net.kyori:adventure-bom:4.21.0"
adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.21.0"
adventure-bom = "net.kyori:adventure-bom:4.23.0"
adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.23.0"
adventure-facet = "net.kyori:adventure-platform-facet:4.3.4"
asm = "org.ow2.asm:asm:9.7.1"
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"
brigadier = "com.velocitypowered:velocity-brigadier:1.0.0-SNAPSHOT"
@ -33,7 +34,7 @@ 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.27.1"
jline = "org.jline:jline-terminal-jansi:3.30.2"
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"
@ -56,7 +57,7 @@ netty-transport-native-epoll = { module = "io.netty:netty-transport-native-epoll
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"
slf4j = "org.slf4j:slf4j-api:2.0.12"
slf4j = "org.slf4j:slf4j-api:2.0.17"
snakeyaml = "org.yaml:snakeyaml:1.33"
spotbugs-annotations = "com.github.spotbugs:spotbugs-annotations:4.7.3"
terminalconsoleappender = "net.minecrell:terminalconsoleappender:1.3.0"

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -1,9 +1,11 @@
import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer
import io.papermc.fill.model.BuildChannel
plugins {
application
id("velocity-init-manifest")
alias(libs.plugins.shadow)
alias(libs.plugins.fill)
}
application {
@ -100,6 +102,7 @@ tasks {
runShadow {
workingDir = file("run").also(File::mkdirs)
standardInput = System.`in`
jvmArgs("-Dvelocity.packet-decode-logging=true")
}
named<JavaExec>("run") {
workingDir = file("run").also(File::mkdirs)
@ -107,6 +110,24 @@ tasks {
}
}
val projectVersion = version as String
fill {
project("velocity")
build {
channel = BuildChannel.STABLE
versionFamily("3.0.0")
version(projectVersion)
downloads {
register("server:default") {
file = tasks.shadowJar.flatMap { it.archiveFile }
nameResolver.set { project, _, version, build -> "$project-$version-$build.jar" }
}
}
}
}
dependencies {
implementation(project(":velocity-api"))
implementation(project(":velocity-native"))

View File

@ -47,6 +47,11 @@ public class Velocity {
System.setProperty("io.netty.native.workdir", System.getProperty("velocity.natives-tmpdir"));
}
// Restore allocator used before Netty 4.2 due to oom issues with the adaptive allocator
if (System.getProperty("io.netty.allocator.type") == null) {
System.setProperty("io.netty.allocator.type", "pooled");
}
// Disable the resource leak detector by default as it reduces performance. Allow the user to
// override this if desired.
if (!VelocityProperties.hasProperty("io.netty.leakDetection.level")) {

View File

@ -81,6 +81,7 @@ 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;
@ -104,7 +105,7 @@ import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.translation.GlobalTranslator;
import net.kyori.adventure.translation.TranslationRegistry;
import net.kyori.adventure.translation.TranslationStore;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bstats.MetricsBase;
@ -337,8 +338,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
}
private void registerTranslations() {
final TranslationRegistry translationRegistry = TranslationRegistry
.create(Key.key("velocity", "translations"));
final TranslationStore.StringBased<MessageFormat> translationRegistry =
TranslationStore.messageFormat(Key.key("velocity", "translations"));
translationRegistry.defaultLocale(Locale.US);
try {
ResourceUtils.visitResources(VelocityServer.class, path -> {

View File

@ -300,27 +300,14 @@ public class VelocityCommandManager implements CommandManager {
);
}
/**
* Returns suggestions to fill in the given command.
*
* @param source the source to execute the command for
* @param cmdLine the partially completed command
* @return a {@link CompletableFuture} eventually completed with a {@link List}, possibly empty
*/
@Override
public CompletableFuture<List<String>> offerSuggestions(final CommandSource source,
final String cmdLine) {
return offerBrigadierSuggestions(source, cmdLine)
.thenApply(suggestions -> Lists.transform(suggestions.getList(), Suggestion::getText));
}
/**
* Returns suggestions to fill in the given command.
*
* @param source the source to execute the command for
* @param cmdLine the partially completed command
* @return a {@link CompletableFuture} eventually completed with {@link Suggestions}, possibly
* empty
*/
@Override
public CompletableFuture<Suggestions> offerBrigadierSuggestions(
final CommandSource source, final String cmdLine) {
Preconditions.checkNotNull(source, "source");

View File

@ -46,6 +46,7 @@ import com.velocitypowered.proxy.protocol.netty.MinecraftCompressDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftCompressorAndLengthEncoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder;
import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueInboundHandler;
import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueOutboundHandler;
@ -368,6 +369,11 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
ensureInEventLoop();
this.state = state;
final MinecraftVarintFrameDecoder frameDecoder = this.channel.pipeline()
.get(MinecraftVarintFrameDecoder.class);
if (frameDecoder != null) {
frameDecoder.setState(state);
}
// If the connection is LEGACY (<1.6), the decoder and encoder are not set.
final MinecraftEncoder minecraftEncoder = this.channel.pipeline()
.get(MinecraftEncoder.class);

View File

@ -34,6 +34,8 @@ import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.command.CommandGraphInjector;
@ -47,6 +49,7 @@ import com.velocitypowered.proxy.connection.util.ConnectionMessages;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder;
import com.velocitypowered.proxy.protocol.packet.AvailableCommandsPacket;
import com.velocitypowered.proxy.protocol.packet.BossBarPacket;
import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket;
@ -149,6 +152,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
MinecraftConnection smc = serverConn.ensureConnected();
smc.setAutoReading(false);
// Even when not auto reading messages are still decoded. Decode them with the correct state
smc.getChannel().pipeline().get(MinecraftVarintFrameDecoder.class).setState(StateRegistry.CONFIG);
smc.getChannel().pipeline().get(MinecraftDecoder.class).setState(StateRegistry.CONFIG);
serverConn.getPlayer().switchToConfigState();
return true;
@ -288,31 +292,14 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
return true;
}
// Register and unregister packets are simply forwarded to the server as-is.
if (PluginMessageUtil.isRegister(packet) || PluginMessageUtil.isUnregister(packet)) {
return false;
}
if (PluginMessageUtil.isMcBrand(packet)) {
PluginMessagePacket rewritten = PluginMessageUtil
.rewriteMinecraftBrand(packet,
server.getVersion(), playerConnection.getProtocolVersion());
playerConnection.write(rewritten);
return true;
}
if (serverConn.getPhase().handle(serverConn, serverConn.getPlayer(), packet)) {
// Handled.
return true;
}
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
if (id == null) {
return false;
}
byte[] copy = ByteBufUtil.getBytes(packet.content());
PluginMessageEvent event = new PluginMessageEvent(serverConn, serverConn.getPlayer(), id, copy);
String channel = packet.getChannel();
PluginMessageEvent event = new PluginMessageEvent(serverConn, serverConn.getPlayer(), channel.indexOf(':') == -1 ? new LegacyChannelIdentifier(channel) : MinecraftChannelIdentifier.from(channel), copy);
server.getEventManager().fire(event).thenAcceptAsync(pme -> {
if (pme.getResult().isAllowed() && !playerConnection.isClosed()) {
PluginMessagePacket copied = new PluginMessagePacket(

View File

@ -40,6 +40,7 @@ import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder;
import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket;
import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket;
import com.velocitypowered.proxy.protocol.packet.DisconnectPacket;
@ -232,6 +233,7 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
final ConnectedPlayer player = serverConn.getPlayer();
final ClientConfigSessionHandler configHandler = (ClientConfigSessionHandler) player.getConnection().getActiveSessionHandler();
smc.getChannel().pipeline().get(MinecraftVarintFrameDecoder.class).setState(StateRegistry.PLAY);
smc.getChannel().pipeline().get(MinecraftDecoder.class).setState(StateRegistry.PLAY);
//noinspection DataFlowIssue
configHandler.handleBackendFinishUpdate(serverConn).thenRunAsync(() -> {

View File

@ -170,7 +170,11 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(KnownPacksPacket packet) {
callConfigurationEvent().thenRun(() -> {
player.getConnectionInFlightOrConnectedServer().ensureConnected().write(packet);
VelocityServerConnection targetServer =
player.getConnectionInFlightOrConnectedServer();
if (targetServer != null) {
targetServer.ensureConnected().write(packet);
}
}).exceptionally(ex -> {
logger.error("Error forwarding known packs response to backend:", ex);
return null;

View File

@ -30,6 +30,8 @@ import com.velocitypowered.api.event.player.TabCompleteEvent;
import com.velocitypowered.api.event.player.configuration.PlayerEnteredConfigurationEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.ConnectionTypes;
import com.velocitypowered.proxy.connection.MinecraftConnection;
@ -339,43 +341,25 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
}
if (!player.getPhase().handle(player, packet, serverConn)) {
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
if (id == null) {
// We don't have any plugins listening on this channel, process the packet now.
if (!player.getPhase().consideredComplete() || !serverConn.getPhase()
.consideredComplete()) {
// The client is trying to send messages too early. This is primarily caused by mods,
// but further aggravated by Velocity. To work around these issues, we will queue any
// non-FML handshake messages to be sent once the FML handshake has completed or the
// JoinGame packet has been received by the proxy, whichever comes first.
//
// We also need to make sure to retain these packets, so they can be flushed
// appropriately.
loginPluginMessages.add(packet.retain());
} else {
// The connection is ready, send the packet now.
backendConn.write(packet.retain());
}
} else {
byte[] copy = ByteBufUtil.getBytes(packet.content());
PluginMessageEvent event = new PluginMessageEvent(player, serverConn, id, copy);
server.getEventManager().fire(event).thenAcceptAsync(pme -> {
if (pme.getResult().isAllowed()) {
PluginMessagePacket message = new PluginMessagePacket(packet.getChannel(),
Unpooled.wrappedBuffer(copy));
if (!player.getPhase().consideredComplete() || !serverConn.getPhase()
.consideredComplete()) {
// We're still processing the connection (see above), enqueue the packet for now.
loginPluginMessages.add(message.retain());
} else {
backendConn.write(message);
}
byte[] copy = ByteBufUtil.getBytes(packet.content());
String channel = packet.getChannel();
PluginMessageEvent event = new PluginMessageEvent(player, serverConn, channel.indexOf(':') == -1 ? new LegacyChannelIdentifier(channel) : MinecraftChannelIdentifier.from(channel), copy);
server.getEventManager().fire(event).thenAcceptAsync(pme -> {
if (pme.getResult().isAllowed()) {
PluginMessagePacket message = new PluginMessagePacket(packet.getChannel(),
Unpooled.wrappedBuffer(copy));
if (!player.getPhase().consideredComplete() || !serverConn.getPhase()
.consideredComplete()) {
// We're still processing the connection (see above), enqueue the packet for now.
loginPluginMessages.add(message.retain());
} else {
backendConn.write(message);
}
}, backendConn.eventLoop()).exceptionally((ex) -> {
logger.error("Exception while handling plugin message packet for {}", player, ex);
return null;
});
}
}
}, backendConn.eventLoop()).exceptionally((ex) -> {
logger.error("Exception while handling plugin message packet for {}", player, ex);
return null;
});
}
}
}

View File

@ -123,6 +123,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.resource.ResourcePackInfoLike;
import net.kyori.adventure.resource.ResourcePackRequest;
import net.kyori.adventure.resource.ResourcePackRequestLike;
@ -145,14 +146,23 @@ import org.jetbrains.annotations.NotNull;
public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, KeyIdentifiable,
VelocityInboundConnection {
public static final int MAX_CLIENTSIDE_PLUGIN_CHANNELS = 1024;
public static final int MAX_CLIENTSIDE_PLUGIN_CHANNELS = Integer.getInteger("velocity.max-clientside-plugin-channels", 1024);
private static final PlainTextComponentSerializer PASS_THRU_TRANSLATE =
PlainTextComponentSerializer.builder().flattener(TranslatableMapper.FLATTENER).build();
static final PermissionProvider DEFAULT_PERMISSIONS = s -> PermissionFunction.ALWAYS_UNDEFINED;
private static final ComponentLogger logger = ComponentLogger.logger(ConnectedPlayer.class);
private final Identity identity = new IdentityImpl();
private static final @NotNull PointersSupplier<ConnectedPlayer> POINTERS_SUPPLIER =
PointersSupplier.<ConnectedPlayer>builder()
.resolving(Identity.UUID, Player::getUniqueId)
.resolving(Identity.NAME, Player::getUsername)
.resolving(Identity.DISPLAY_NAME, player -> Component.text(player.getUsername()))
.resolving(Identity.LOCALE, Player::getEffectiveLocale)
.resolving(PermissionChecker.POINTER, Player::getPermissionChecker)
.resolving(FacetPointers.TYPE, player -> Type.PLAYER)
.build();
/**
* The actual Minecraft connection. This is actually a wrapper object around the Netty channel.
*/
@ -181,14 +191,6 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
private final ResourcePackHandler resourcePackHandler;
private final BundleDelimiterHandler bundleHandler = new BundleDelimiterHandler(this);
private final @NotNull Pointers pointers =
Player.super.pointers().toBuilder()
.withDynamic(Identity.UUID, this::getUniqueId)
.withDynamic(Identity.NAME, this::getUsername)
.withDynamic(Identity.DISPLAY_NAME, () -> Component.text(this.getUsername()))
.withDynamic(Identity.LOCALE, this::getEffectiveLocale)
.withStatic(PermissionChecker.POINTER, getPermissionChecker())
.withStatic(FacetPointers.TYPE, Type.PLAYER).build();
private @Nullable String clientBrand;
private @Nullable Locale effectiveLocale;
private final @Nullable IdentifiedKey playerKey;
@ -257,7 +259,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
@Override
public @NonNull Identity identity() {
return this.identity;
return Identity.identity(this.getUniqueId());
}
@Override
@ -363,7 +365,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
@Override
public @NotNull Pointers pointers() {
return this.pointers;
return POINTERS_SUPPLIER.view(this);
}
@Override
@ -396,14 +398,20 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
}
/**
* Translates the message in the user's locale.
* Translates the message in the user's locale, falling back to the default locale if not set.
*
* @param message the message to translate
* @return the translated message
*/
public Component translateMessage(Component message) {
Locale locale = ClosestLocaleMatcher.INSTANCE
.lookupClosest(getEffectiveLocale() == null ? Locale.getDefault() : getEffectiveLocale());
Locale locale = this.getEffectiveLocale();
if (locale == null && settings != null) {
locale = settings.getLocale();
}
if (locale == null) {
locale = Locale.getDefault();
}
locale = ClosestLocaleMatcher.INSTANCE.lookupClosest(locale);
return GlobalTranslator.render(message, locale);
}
@ -1361,14 +1369,6 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
return playerKey;
}
private class IdentityImpl implements Identity {
@Override
public @NonNull UUID uuid() {
return ConnectedPlayer.this.getUniqueId();
}
}
@Override
public ProtocolState getProtocolState() {
return connection.getState().toProtocolState();

View File

@ -51,7 +51,7 @@ public class BackendChannelInitializer extends ChannelInitializer<Channel> {
@Override
protected void initChannel(Channel ch) {
ch.pipeline()
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder(ProtocolUtils.Direction.CLIENTBOUND))
.addLast(READ_TIMEOUT,
new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(),
TimeUnit.MILLISECONDS))

View File

@ -58,7 +58,7 @@ public class ServerChannelInitializer extends ChannelInitializer<Channel> {
protected void initChannel(final Channel ch) {
ch.pipeline()
.addLast(LEGACY_PING_DECODER, new LegacyPingDecoder())
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder(ProtocolUtils.Direction.SERVERBOUND))
.addLast(READ_TIMEOUT,
new ReadTimeoutHandler(this.server.getConfiguration().getReadTimeout(),
TimeUnit.MILLISECONDS))

View File

@ -116,7 +116,7 @@ public enum TransportType {
return NIO;
}
if (IoUring.isAvailable() && !Boolean.getBoolean("velocity.disable-iouring-transport")) {
if (IoUring.isAvailable() && Boolean.getBoolean("velocity.enable-iouring-transport")) {
return IO_URING;
}

View File

@ -64,10 +64,13 @@ public enum ProtocolUtils {
// before 1.16
.value(JSONOptions.EMIT_RGB, Boolean.FALSE)
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.VALUE_FIELD)
.value(JSONOptions.EMIT_CLICK_EVENT_TYPE, JSONOptions.ClickEventValueMode.CAMEL_CASE)
// before 1.20.3
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.FALSE)
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.FALSE)
.value(JSONOptions.VALIDATE_STRICT_EVENTS, Boolean.FALSE)
// before 1.21.5
.value(JSONOptions.EMIT_CHANGE_PAGE_CLICK_EVENT_PAGE_AS_STRING, Boolean.TRUE)
.build()
)
.build();
@ -79,11 +82,14 @@ public enum ProtocolUtils {
// after 1.16
.value(JSONOptions.EMIT_RGB, Boolean.TRUE)
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.CAMEL_CASE)
.value(JSONOptions.EMIT_CLICK_EVENT_TYPE, JSONOptions.ClickEventValueMode.CAMEL_CASE)
.value(JSONOptions.EMIT_CLICK_EVENT_TYPE, JSONOptions.ClickEventValueMode.CAMEL_CASE)
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_KEY_AS_TYPE_AND_UUID_AS_ID, true)
// before 1.20.3
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.FALSE)
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.FALSE)
.value(JSONOptions.VALIDATE_STRICT_EVENTS, Boolean.FALSE)
// before 1.21.5
.value(JSONOptions.EMIT_CHANGE_PAGE_CLICK_EVENT_PAGE_AS_STRING, Boolean.TRUE)
.build()
)
.build();
@ -96,10 +102,13 @@ public enum ProtocolUtils {
.value(JSONOptions.EMIT_RGB, Boolean.TRUE)
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.CAMEL_CASE)
.value(JSONOptions.EMIT_CLICK_EVENT_TYPE, JSONOptions.ClickEventValueMode.CAMEL_CASE)
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_KEY_AS_TYPE_AND_UUID_AS_ID, true)
// after 1.20.3
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.TRUE)
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.TRUE)
.value(JSONOptions.VALIDATE_STRICT_EVENTS, Boolean.TRUE)
// before 1.21.5
.value(JSONOptions.EMIT_CHANGE_PAGE_CLICK_EVENT_PAGE_AS_STRING, Boolean.TRUE)
.build()
)
.build();
@ -118,6 +127,7 @@ public enum ProtocolUtils {
// after 1.21.5
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_KEY_AS_TYPE_AND_UUID_AS_ID, Boolean.FALSE)
.value(JSONOptions.VALIDATE_STRICT_EVENTS, Boolean.TRUE)
.value(JSONOptions.EMIT_CHANGE_PAGE_CLICK_EVENT_PAGE_AS_STRING, Boolean.FALSE)
.build()
)
.build();

View File

@ -40,6 +40,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_4;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_5;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_6;
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;
@ -87,6 +88,7 @@ import com.velocitypowered.proxy.protocol.packet.StatusResponsePacket;
import com.velocitypowered.proxy.protocol.packet.TabCompleteRequestPacket;
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponsePacket;
import com.velocitypowered.proxy.protocol.packet.TransferPacket;
import com.velocitypowered.proxy.protocol.packet.UpdateTeamsPacket;
import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket;
import com.velocitypowered.proxy.protocol.packet.chat.ChatAcknowledgementPacket;
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletionPacket;
@ -256,7 +258,8 @@ public enum StateRegistry {
map(0x09, MINECRAFT_1_19_4, false),
map(0x0A, MINECRAFT_1_20_2, false),
map(0x0B, MINECRAFT_1_20_5, false),
map(0x0D, MINECRAFT_1_21_2, false));
map(0x0D, MINECRAFT_1_21_2, false),
map(0x0E, MINECRAFT_1_21_6, false));
serverbound.register(
LegacyChatPacket.class,
LegacyChatPacket::new,
@ -269,7 +272,8 @@ public enum StateRegistry {
ChatAcknowledgementPacket.class,
ChatAcknowledgementPacket::new,
map(0x03, MINECRAFT_1_19_3, false),
map(0x04, MINECRAFT_1_21_2, false));
map(0x04, MINECRAFT_1_21_2, false),
map(0x05, MINECRAFT_1_21_6, false));
serverbound.register(KeyedPlayerCommandPacket.class, KeyedPlayerCommandPacket::new,
map(0x03, MINECRAFT_1_19, false),
map(0x04, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false));
@ -279,16 +283,19 @@ public enum StateRegistry {
serverbound.register(SessionPlayerCommandPacket.class, SessionPlayerCommandPacket::new,
map(0x04, MINECRAFT_1_19_3, false),
map(0x05, MINECRAFT_1_20_5, false),
map(0x06, MINECRAFT_1_21_2, false));
map(0x06, MINECRAFT_1_21_2, false),
map(0x07, MINECRAFT_1_21_6, false));
serverbound.register(UnsignedPlayerCommandPacket.class, UnsignedPlayerCommandPacket::new,
map(0x04, MINECRAFT_1_20_5, false),
map(0x05, MINECRAFT_1_21_2, false));
map(0x05, MINECRAFT_1_21_2, false),
map(0x06, MINECRAFT_1_21_6, 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(0x07, MINECRAFT_1_21_2, false),
map(0x08, MINECRAFT_1_21_6, false));
serverbound.register(
ClientSettingsPacket.class,
ClientSettingsPacket::new,
@ -303,11 +310,13 @@ public enum StateRegistry {
map(0x08, MINECRAFT_1_19_4, false),
map(0x09, MINECRAFT_1_20_2, false),
map(0x0A, MINECRAFT_1_20_5, false),
map(0x0C, MINECRAFT_1_21_2, false));
map(0x0C, MINECRAFT_1_21_2, false),
map(0x0D, MINECRAFT_1_21_6, false));
serverbound.register(
ServerboundCookieResponsePacket.class, ServerboundCookieResponsePacket::new,
map(0x11, MINECRAFT_1_20_5, false),
map(0x13, MINECRAFT_1_21_2, false));
map(0x13, MINECRAFT_1_21_2, false),
map(0x14, MINECRAFT_1_21_6, false));
serverbound.register(
PluginMessagePacket.class,
PluginMessagePacket::new,
@ -325,7 +334,8 @@ public enum StateRegistry {
map(0x0F, MINECRAFT_1_20_2, false),
map(0x10, MINECRAFT_1_20_3, false),
map(0x12, MINECRAFT_1_20_5, false),
map(0x14, MINECRAFT_1_21_2, false));
map(0x14, MINECRAFT_1_21_2, false),
map(0x15, MINECRAFT_1_21_6, false));
serverbound.register(
KeepAlivePacket.class,
KeepAlivePacket::new,
@ -344,7 +354,8 @@ public enum StateRegistry {
map(0x14, MINECRAFT_1_20_2, false),
map(0x15, MINECRAFT_1_20_3, false),
map(0x18, MINECRAFT_1_20_5, false),
map(0x1A, MINECRAFT_1_21_2, false));
map(0x1A, MINECRAFT_1_21_2, false),
map(0x1B, MINECRAFT_1_21_6, false));
serverbound.register(
ResourcePackResponsePacket.class,
ResourcePackResponsePacket::new,
@ -361,12 +372,14 @@ public enum StateRegistry {
map(0x28, MINECRAFT_1_20_3, false),
map(0x2B, MINECRAFT_1_20_5, false),
map(0x2D, MINECRAFT_1_21_2, false),
map(0x2F, MINECRAFT_1_21_4, false));
map(0x2F, MINECRAFT_1_21_4, false),
map(0x30, MINECRAFT_1_21_6, 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(0x0E, MINECRAFT_1_21_2, false),
map(0x0F, MINECRAFT_1_21_6, false));
clientbound.register(
BossBarPacket.class,
@ -731,6 +744,22 @@ public enum StateRegistry {
ClientboundServerLinksPacket::new,
map(0x7B, MINECRAFT_1_21, false),
map(0x82, MINECRAFT_1_21_2, false));
clientbound.register(UpdateTeamsPacket.class, UpdateTeamsPacket::new,
map(0x41, ProtocolVersion.MINECRAFT_1_9, true),
map(0x43, ProtocolVersion.MINECRAFT_1_12, true),
map(0x44, ProtocolVersion.MINECRAFT_1_12_1, true),
map(0x47, ProtocolVersion.MINECRAFT_1_13, true),
map(0x4B, ProtocolVersion.MINECRAFT_1_14, true),
map(0x4C, ProtocolVersion.MINECRAFT_1_15, true),
map(0x55, ProtocolVersion.MINECRAFT_1_17, true),
map(0x58, ProtocolVersion.MINECRAFT_1_19_1, true),
map(0x56, ProtocolVersion.MINECRAFT_1_19_3, true),
map(0x5A, ProtocolVersion.MINECRAFT_1_19_4, true),
map(0x5C, ProtocolVersion.MINECRAFT_1_20_2, true),
map(0x5E, ProtocolVersion.MINECRAFT_1_20_3, true),
map(0x60, ProtocolVersion.MINECRAFT_1_20_5, true),
map(0x67, ProtocolVersion.MINECRAFT_1_21_2, true)
);
}
},
LOGIN {

View File

@ -39,6 +39,7 @@ public class MinecraftCompressDecoder extends MessageToMessageDecoder<ByteBuf> {
private static final int UNCOMPRESSED_CAP =
Boolean.getBoolean("velocity.increased-compression-cap")
? HARD_MAXIMUM_UNCOMPRESSED_SIZE : VANILLA_MAXIMUM_UNCOMPRESSED_SIZE;
private static final boolean SKIP_COMPRESSION_VALIDATION = Boolean.getBoolean("velocity.skip-uncompressed-packet-size-validation");
private int threshold;
private final VelocityCompressor compressor;
@ -52,9 +53,11 @@ public class MinecraftCompressDecoder extends MessageToMessageDecoder<ByteBuf> {
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
int claimedUncompressedSize = ProtocolUtils.readVarInt(in);
if (claimedUncompressedSize == 0) {
int actualUncompressedSize = in.readableBytes();
checkFrame(actualUncompressedSize < threshold, "Actual uncompressed size %s is greater than"
+ " threshold %s", actualUncompressedSize, threshold);
if (!SKIP_COMPRESSION_VALIDATION) {
int actualUncompressedSize = in.readableBytes();
checkFrame(actualUncompressedSize < threshold, "Actual uncompressed size %s is greater than"
+ " threshold %s", actualUncompressedSize, threshold);
}
// This message is not compressed.
out.add(in.retain());
return;

View File

@ -135,7 +135,7 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter {
private String getExtraConnectionDetail(int packetId) {
return "Direction " + direction + " Protocol " + registry.version + " State " + state
+ " ID " + Integer.toHexString(packetId);
+ " ID 0x" + Integer.toHexString(packetId);
}
public void setProtocolVersion(ProtocolVersion protocolVersion) {

View File

@ -19,21 +19,51 @@ 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.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
import com.velocitypowered.proxy.util.except.QuietRuntimeException;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.CorruptedFrameException;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Frames Minecraft server packets which are prefixed by a 21-bit VarInt encoding.
*/
public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
private static final Logger LOGGER = LogManager.getLogger(MinecraftVarintFrameDecoder.class);
private static final QuietRuntimeException FRAME_DECODER_FAILED =
new QuietRuntimeException("A packet frame decoder failed. For more information, launch "
+ "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 VARINT_TOO_BIG =
new QuietDecoderException("VarInt too big");
private static final QuietDecoderException UNKNOWN_PACKET =
new QuietDecoderException("Unknown packet");
private final ProtocolUtils.Direction direction;
private final StateRegistry.PacketRegistry.ProtocolRegistry registry;
private StateRegistry state;
/**
* Creates a new {@code MinecraftVarintFrameDecoder} decoding packets from the specified {@code Direction}.
*
* @param direction the direction from which we decode from
*/
public MinecraftVarintFrameDecoder(ProtocolUtils.Direction direction) {
this.direction = direction;
this.registry = StateRegistry.HANDSHAKE.getProtocolRegistry(
direction, ProtocolVersion.MINIMUM_VERSION);
this.state = StateRegistry.HANDSHAKE;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
@ -62,6 +92,43 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
throw BAD_PACKET_LENGTH;
}
if (length > 0) {
if (state == StateRegistry.HANDSHAKE && direction == ProtocolUtils.Direction.SERVERBOUND) {
StateRegistry.PacketRegistry.ProtocolRegistry registry =
state.getProtocolRegistry(direction, ProtocolVersion.MINIMUM_VERSION);
final int index = in.readerIndex();
final int packetId = readRawVarInt21(in);
// Index hasn't changed, we've read nothing
if (index == in.readerIndex()) {
in.resetReaderIndex();
return;
}
final int payloadLength = length - ProtocolUtils.varIntBytes(packetId);
MinecraftPacket packet = registry.createPacket(packetId);
// We handle every packet in this phase, if you said something we don't know, something is really wrong
if (packet == null) {
throw UNKNOWN_PACKET;
}
// We 'technically' have the incoming bytes of a payload here, and so, these can actually parse
// the packet if needed, so, we'll take advantage of the existing methods
int expectedMinLen = packet.expectedMinLength(in, direction, registry.version);
int expectedMaxLen = packet.expectedMaxLength(in, direction, registry.version);
if (expectedMaxLen != -1 && payloadLength > expectedMaxLen) {
throw handleOverflow(packet, expectedMaxLen, in.readableBytes());
}
if (payloadLength < expectedMinLen) {
throw handleUnderflow(packet, expectedMaxLen, in.readableBytes());
}
in.readerIndex(index);
}
}
// note that zero-length packets are ignored
if (length > 0) {
if (in.readableBytes() < length) {
@ -72,6 +139,16 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (MinecraftDecoder.DEBUG) {
LOGGER.atWarn()
.withThrowable(cause)
.log("Exception caught while decoding frame for {}", ctx.channel().remoteAddress());
}
super.exceptionCaught(ctx, cause);
}
/**
* Reads a VarInt from the buffer of up to 21 bits in size.
*
@ -141,4 +218,26 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
}
return result | (tmp & 0x7F) << 14;
}
private Exception handleOverflow(MinecraftPacket packet, int expected, int actual) {
if (MinecraftDecoder.DEBUG) {
return new CorruptedFrameException("Packet sent for " + packet.getClass() + " was too "
+ "big (expected " + expected + " bytes, got " + actual + " bytes)");
} else {
return FRAME_DECODER_FAILED;
}
}
private Exception handleUnderflow(MinecraftPacket packet, int expected, int actual) {
if (MinecraftDecoder.DEBUG) {
return new CorruptedFrameException("Packet sent for " + packet.getClass() + " was too "
+ "small (expected " + expected + " bytes, got " + actual + " bytes)");
} else {
return FRAME_DECODER_FAILED;
}
}
public void setState(StateRegistry stateRegistry) {
this.state = stateRegistry;
}
}

View File

@ -50,12 +50,14 @@ import java.util.Deque;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
public class AvailableCommandsPacket implements MinecraftPacket {
private static final Command<CommandSource> PLACEHOLDER_COMMAND = source -> 0;
private static final Predicate<CommandSource> PLACEHOLDER_REQUIREMENT = source -> true;
private static final byte NODE_TYPE_ROOT = 0x00;
private static final byte NODE_TYPE_LITERAL = 0x01;
@ -65,6 +67,7 @@ public class AvailableCommandsPacket implements MinecraftPacket {
private static final byte FLAG_EXECUTABLE = 0x04;
private static final byte FLAG_IS_REDIRECT = 0x08;
private static final byte FLAG_HAS_SUGGESTIONS = 0x10;
private static final byte FLAG_IS_RESTRICTED = 0x20;
private @MonotonicNonNull RootCommandNode<CommandSource> rootNode;
@ -146,6 +149,9 @@ public class AvailableCommandsPacket implements MinecraftPacket {
if (node.getCommand() != null) {
flags |= FLAG_EXECUTABLE;
}
if (node.getRequirement() == PLACEHOLDER_REQUIREMENT) {
flags |= FLAG_IS_RESTRICTED;
}
if (node instanceof LiteralCommandNode<?>) {
flags |= NODE_TYPE_LITERAL;
@ -289,6 +295,11 @@ public class AvailableCommandsPacket implements MinecraftPacket {
args.executes(PLACEHOLDER_COMMAND);
}
// If restricted, add empty requirement
if ((flags & FLAG_IS_RESTRICTED) != 0) {
args.requires(PLACEHOLDER_REQUIREMENT);
}
this.built = args.build();
}
}

View File

@ -106,4 +106,16 @@ public class HandshakePacket implements MinecraftPacket {
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
@Override
public int expectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
return 7;
}
@Override
public int expectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
return 9 + (MAXIMUM_HOSTNAME_LENGTH * 3);
}
}

View File

@ -0,0 +1,233 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.Player;
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 net.kyori.adventure.text.Component;
import java.util.List;
public class UpdateTeamsPacket implements MinecraftPacket {
private String name;
private Mode mode;
private Component displayName;
private Component prefix;
private Component suffix;
private NameTagVisibility nameTagVisibility;
private CollisionRule collisionRule;
private int color;
private byte friendlyFlags;
private List<String> players;
public UpdateTeamsPacket(String name, Mode mode, Component displayName, Component prefix, Component suffix, NameTagVisibility nameTagVisibility, CollisionRule collisionRule, int color, byte friendlyFlags, List<String> players) {
this.name = name;
this.mode = mode;
this.displayName = displayName;
this.prefix = prefix;
this.suffix = suffix;
this.nameTagVisibility = nameTagVisibility;
this.collisionRule = collisionRule;
this.color = color;
this.friendlyFlags = friendlyFlags;
this.players = players;
}
public UpdateTeamsPacket() {
}
@Override
public void decode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
throw new UnsupportedOperationException("Packet is not implemented");
}
@Override
public boolean handle(MinecraftSessionHandler minecraftSessionHandler) {
return false;
}
@Override
public void encode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeString(byteBuf, name);
byteBuf.writeByte(mode.ordinal());
switch (mode) {
case CREATE, UPDATE:
new ComponentHolder(protocolVersion, displayName).write(byteBuf);
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_13)) {
new ComponentHolder(protocolVersion, prefix).write(byteBuf);
new ComponentHolder(protocolVersion, suffix).write(byteBuf);
}
byteBuf.writeByte(friendlyFlags);
ProtocolUtils.writeString(byteBuf, nameTagVisibility.getValue());
ProtocolUtils.writeString(byteBuf, collisionRule.getValue());
if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_12_2)) {
ProtocolUtils.writeVarInt(byteBuf, color);
new ComponentHolder(protocolVersion, prefix).write(byteBuf);
new ComponentHolder(protocolVersion, suffix).write(byteBuf);
} else {
byteBuf.writeByte((byte) color);
}
ProtocolUtils.writeVarInt(byteBuf, players.size());
for (String player : players) {
ProtocolUtils.writeString(byteBuf, player);
}
break;
case ADD_PLAYER, REMOVE_PLAYER:
ProtocolUtils.writeVarInt(byteBuf, players.size());
for (String player : players) {
ProtocolUtils.writeString(byteBuf, player);
}
break;
case REMOVE:
break;
}
}
public enum Mode {
CREATE,
REMOVE,
UPDATE,
ADD_PLAYER,
REMOVE_PLAYER,
}
public enum NameTagVisibility {
ALWAYS("always"),
NEVER("never"),
HIDE_FOR_OTHER_TEAMS("hideForOtherTeams"),
HIDE_FOR_OWN_TEAM("hideForOwnTeam");
private final String value;
NameTagVisibility(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public enum CollisionRule {
ALWAYS("always"),
NEVER("never"),
PUSH_OTHER_TEAMS("pushOtherTeams"),
PUSH_OWN_TEAM("pushOwnTeam");
private final String value;
CollisionRule(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public String getName() {
return name;
}
public Mode getMode() {
return mode;
}
public Component getDisplayName() {
return displayName;
}
public Component getPrefix() {
return prefix;
}
public Component getSuffix() {
return suffix;
}
public NameTagVisibility getNameTagVisibility() {
return nameTagVisibility;
}
public CollisionRule getCollisionRule() {
return collisionRule;
}
public int getColor() {
return color;
}
public byte getFriendlyFlags() {
return friendlyFlags;
}
public List<String> getPlayers() {
return players;
}
public void setName(String name) {
this.name = name;
}
public void setMode(Mode mode) {
this.mode = mode;
}
public void setDisplayName(Component displayName) {
this.displayName = displayName;
}
public void setPrefix(Component prefix) {
this.prefix = prefix;
}
public void setSuffix(Component suffix) {
this.suffix = suffix;
}
public void setNameTagVisibility(NameTagVisibility nameTagVisibility) {
this.nameTagVisibility = nameTagVisibility;
}
public void setCollisionRule(CollisionRule collisionRule) {
this.collisionRule = collisionRule;
}
public void setColor(int color) {
this.color = color;
}
public void setFriendlyFlags(byte friendlyFlags) {
this.friendlyFlags = friendlyFlags;
}
public void setPlayers(List<String> players) {
this.players = players;
}
}

View File

@ -23,6 +23,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_4;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_3;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_5;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_5;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_6;
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.id;
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.mapSet;
import static com.velocitypowered.proxy.protocol.packet.brigadier.DoubleArgumentPropertySerializer.DOUBLE;
@ -164,6 +165,7 @@ public class ArgumentPropertyRegistry {
return i;
}
}
throw new IllegalArgumentException("Argument type identifier " + id + " unknown.");
} else {
String identifier = ProtocolUtils.readString(buf);
for (ArgumentIdentifier i : byIdentifier.keySet()) {
@ -207,68 +209,79 @@ public class ArgumentPropertyRegistry {
empty(id("minecraft:item_stack", mapSet(MINECRAFT_1_19, 14)));
empty(id("minecraft:item_predicate", mapSet(MINECRAFT_1_19, 15)));
empty(id("minecraft:color", mapSet(MINECRAFT_1_19, 16)));
empty(id("minecraft:component", mapSet(MINECRAFT_1_19, 17)));
empty(id("minecraft:style", mapSet(MINECRAFT_1_20_3, 18))); // added 1.20.3
empty(id("minecraft:message", mapSet(MINECRAFT_1_20_3, 19), mapSet(MINECRAFT_1_19, 18)));
empty(id("minecraft:nbt_compound_tag", mapSet(MINECRAFT_1_20_3, 20), mapSet(MINECRAFT_1_19, 19))); // added in 1.14
empty(id("minecraft:nbt_tag", mapSet(MINECRAFT_1_20_3, 21), mapSet(MINECRAFT_1_19, 20))); // added in 1.14
empty(id("minecraft:nbt_path", mapSet(MINECRAFT_1_20_3, 22), mapSet(MINECRAFT_1_19, 21)));
empty(id("minecraft:objective", mapSet(MINECRAFT_1_20_3, 23), mapSet(MINECRAFT_1_19, 22)));
empty(id("minecraft:objective_criteria", mapSet(MINECRAFT_1_20_3, 24), mapSet(MINECRAFT_1_19, 23)));
empty(id("minecraft:operation", mapSet(MINECRAFT_1_20_3, 25), mapSet(MINECRAFT_1_19, 24)));
empty(id("minecraft:particle", mapSet(MINECRAFT_1_20_3, 26), mapSet(MINECRAFT_1_19, 25)));
empty(id("minecraft:angle", mapSet(MINECRAFT_1_20_3, 27), mapSet(MINECRAFT_1_19, 26))); // added in 1.16.2
empty(id("minecraft:rotation", mapSet(MINECRAFT_1_20_3, 28), mapSet(MINECRAFT_1_19, 27)));
empty(id("minecraft:scoreboard_slot", mapSet(MINECRAFT_1_20_3, 29), mapSet(MINECRAFT_1_19, 28)));
empty(id("minecraft:score_holder", mapSet(MINECRAFT_1_20_3, 30), mapSet(MINECRAFT_1_19, 29)), ByteArgumentPropertySerializer.BYTE);
empty(id("minecraft:swizzle", mapSet(MINECRAFT_1_20_3, 31), mapSet(MINECRAFT_1_19, 30)));
empty(id("minecraft:team", mapSet(MINECRAFT_1_20_3, 32), mapSet(MINECRAFT_1_19, 31)));
empty(id("minecraft:item_slot", mapSet(MINECRAFT_1_20_3, 33), mapSet(MINECRAFT_1_19, 32)));
empty(id("minecraft:item_slots", mapSet(MINECRAFT_1_20_5, 34))); // added 1.20.5
empty(id("minecraft:resource_location", mapSet(MINECRAFT_1_20_5, 35), mapSet(MINECRAFT_1_20_3, 34), mapSet(MINECRAFT_1_19, 33)));
empty(id("minecraft:component", mapSet(MINECRAFT_1_21_6, 18), mapSet(MINECRAFT_1_19, 17)));
empty(id("minecraft:style", mapSet(MINECRAFT_1_21_6, 19), mapSet(MINECRAFT_1_20_3, 18))); // added 1.20.3
empty(id("minecraft:message", mapSet(MINECRAFT_1_21_6, 20), mapSet(MINECRAFT_1_20_3, 19), mapSet(MINECRAFT_1_19, 18)));
empty(id("minecraft:nbt_compound_tag", mapSet(MINECRAFT_1_21_6, 21), mapSet(MINECRAFT_1_20_3, 20), mapSet(MINECRAFT_1_19, 19))); // added in 1.14
empty(id("minecraft:nbt_tag", mapSet(MINECRAFT_1_21_6, 22), mapSet(MINECRAFT_1_20_3, 21), mapSet(MINECRAFT_1_19, 20))); // added in 1.14
empty(id("minecraft:nbt_path", mapSet(MINECRAFT_1_21_6, 23), mapSet(MINECRAFT_1_20_3, 22), mapSet(MINECRAFT_1_19, 21)));
empty(id("minecraft:objective", mapSet(MINECRAFT_1_21_6, 24), mapSet(MINECRAFT_1_20_3, 23), mapSet(MINECRAFT_1_19, 22)));
empty(id("minecraft:objective_criteria", mapSet(MINECRAFT_1_21_6, 25), mapSet(MINECRAFT_1_20_3, 24), mapSet(MINECRAFT_1_19, 23)));
empty(id("minecraft:operation", mapSet(MINECRAFT_1_21_6, 26), mapSet(MINECRAFT_1_20_3, 25), mapSet(MINECRAFT_1_19, 24)));
empty(id("minecraft:particle", mapSet(MINECRAFT_1_21_6, 27), mapSet(MINECRAFT_1_20_3, 26), mapSet(MINECRAFT_1_19, 25)));
empty(id("minecraft:angle", mapSet(MINECRAFT_1_21_6, 28), mapSet(MINECRAFT_1_20_3, 27), mapSet(MINECRAFT_1_19, 26))); // added in 1.16.2
empty(id("minecraft:rotation", mapSet(MINECRAFT_1_21_6, 29), mapSet(MINECRAFT_1_20_3, 28), mapSet(MINECRAFT_1_19, 27)));
empty(id("minecraft:scoreboard_slot", mapSet(MINECRAFT_1_21_6, 30), mapSet(MINECRAFT_1_20_3, 29), mapSet(MINECRAFT_1_19, 28)));
empty(id("minecraft:score_holder", mapSet(MINECRAFT_1_21_6, 31), mapSet(MINECRAFT_1_20_3, 30), mapSet(MINECRAFT_1_19, 29)),
ByteArgumentPropertySerializer.BYTE);
empty(id("minecraft:swizzle", mapSet(MINECRAFT_1_21_6, 32), mapSet(MINECRAFT_1_20_3, 31), mapSet(MINECRAFT_1_19, 30)));
empty(id("minecraft:team", mapSet(MINECRAFT_1_21_6, 33), mapSet(MINECRAFT_1_20_3, 32), mapSet(MINECRAFT_1_19, 31)));
empty(id("minecraft:item_slot", mapSet(MINECRAFT_1_21_6, 34), mapSet(MINECRAFT_1_20_3, 33), mapSet(MINECRAFT_1_19, 32)));
empty(id("minecraft:item_slots", mapSet(MINECRAFT_1_21_6, 35), mapSet(MINECRAFT_1_20_5, 34))); // added 1.20.5
empty(id("minecraft:resource_location", mapSet(MINECRAFT_1_21_6, 36), mapSet(MINECRAFT_1_20_5, 35), mapSet(MINECRAFT_1_20_3, 34),
mapSet(MINECRAFT_1_19, 33)));
empty(id("minecraft:mob_effect", mapSet(MINECRAFT_1_19_3, -1), mapSet(MINECRAFT_1_19, 34)));
empty(id("minecraft:function", mapSet(MINECRAFT_1_20_5, 36), mapSet(MINECRAFT_1_20_3, 35), mapSet(MINECRAFT_1_19_3, 34),
mapSet(MINECRAFT_1_19, 35)));
empty(id("minecraft:entity_anchor", mapSet(MINECRAFT_1_20_5, 37), mapSet(MINECRAFT_1_20_3, 36), mapSet(MINECRAFT_1_19_3, 35),
mapSet(MINECRAFT_1_19, 36)));
empty(id("minecraft:int_range", mapSet(MINECRAFT_1_20_5, 38), mapSet(MINECRAFT_1_20_3, 37), mapSet(MINECRAFT_1_19_3, 36),
mapSet(MINECRAFT_1_19, 37)));
empty(id("minecraft:float_range", mapSet(MINECRAFT_1_20_5, 39), mapSet(MINECRAFT_1_20_3, 38), mapSet(MINECRAFT_1_19_3, 37),
mapSet(MINECRAFT_1_19, 38)));
empty(id("minecraft:function", mapSet(MINECRAFT_1_21_6, 37), mapSet(MINECRAFT_1_20_5, 36), mapSet(MINECRAFT_1_20_3, 35),
mapSet(MINECRAFT_1_19_3, 34), mapSet(MINECRAFT_1_19, 35)));
empty(id("minecraft:entity_anchor", mapSet(MINECRAFT_1_21_6, 38), mapSet(MINECRAFT_1_20_5, 37), mapSet(MINECRAFT_1_20_3, 36),
mapSet(MINECRAFT_1_19_3, 35), mapSet(MINECRAFT_1_19, 36)));
empty(id("minecraft:int_range", mapSet(MINECRAFT_1_21_6, 39), mapSet(MINECRAFT_1_20_5, 38), mapSet(MINECRAFT_1_20_3, 37),
mapSet(MINECRAFT_1_19_3, 36), mapSet(MINECRAFT_1_19, 37)));
empty(id("minecraft:float_range", mapSet(MINECRAFT_1_21_6, 40), mapSet(MINECRAFT_1_20_5, 39), mapSet(MINECRAFT_1_20_3, 38),
mapSet(MINECRAFT_1_19_3, 37), mapSet(MINECRAFT_1_19, 38)));
empty(id("minecraft:item_enchantment", mapSet(MINECRAFT_1_19_3, -1), mapSet(MINECRAFT_1_19, 39)));
empty(id("minecraft:entity_summon", mapSet(MINECRAFT_1_19_3, -1), mapSet(MINECRAFT_1_19, 40)));
empty(id("minecraft:dimension", mapSet(MINECRAFT_1_20_5, 40), mapSet(MINECRAFT_1_20_3, 39), mapSet(MINECRAFT_1_19_3, 38),
mapSet(MINECRAFT_1_19, 41)));
empty(id("minecraft:gamemode", mapSet(MINECRAFT_1_20_5, 41), mapSet(MINECRAFT_1_20_3, 40), mapSet(MINECRAFT_1_19_3, 39))); // 1.19.3
empty(id("minecraft:dimension", mapSet(MINECRAFT_1_21_6, 41), mapSet(MINECRAFT_1_20_5, 40), mapSet(MINECRAFT_1_20_3, 39),
mapSet(MINECRAFT_1_19_3, 38), mapSet(MINECRAFT_1_19, 41)));
empty(id("minecraft:gamemode", mapSet(MINECRAFT_1_21_6, 42), mapSet(MINECRAFT_1_20_5, 41), mapSet(MINECRAFT_1_20_3, 40),
mapSet(MINECRAFT_1_19_3, 39))); // 1.19.3
empty(id("minecraft:time", mapSet(MINECRAFT_1_20_5, 42), mapSet(MINECRAFT_1_20_3, 41), mapSet(MINECRAFT_1_19_3, 40),
mapSet(MINECRAFT_1_19, 42)), TimeArgumentSerializer.TIME); // added in 1.14
empty(id("minecraft:time", mapSet(MINECRAFT_1_21_6, 43), mapSet(MINECRAFT_1_20_5, 42), mapSet(MINECRAFT_1_20_3, 41),
mapSet(MINECRAFT_1_19_3, 40), mapSet(MINECRAFT_1_19, 42)), TimeArgumentSerializer.TIME); // added in 1.14
register(id("minecraft:resource_or_tag", mapSet(MINECRAFT_1_20_5, 43), mapSet(MINECRAFT_1_20_3, 42), mapSet(MINECRAFT_1_19_3, 41),
mapSet(MINECRAFT_1_19, 43)), RegistryKeyArgument.class, RegistryKeyArgumentSerializer.REGISTRY);
register(id("minecraft:resource_or_tag_key", mapSet(MINECRAFT_1_20_5, 44), mapSet(MINECRAFT_1_20_3, 43), mapSet(MINECRAFT_1_19_3, 42)),
register(id("minecraft:resource_or_tag", mapSet(MINECRAFT_1_21_6, 44), mapSet(MINECRAFT_1_20_5, 43), mapSet(MINECRAFT_1_20_3, 42),
mapSet(MINECRAFT_1_19_3, 41), mapSet(MINECRAFT_1_19, 43)), RegistryKeyArgument.class, RegistryKeyArgumentSerializer.REGISTRY);
register(id("minecraft:resource_or_tag_key", mapSet(MINECRAFT_1_21_6, 45), mapSet(MINECRAFT_1_20_5, 44), mapSet(MINECRAFT_1_20_3, 43),
mapSet(MINECRAFT_1_19_3, 42)),
RegistryKeyArgumentList.ResourceOrTagKey.class,
RegistryKeyArgumentList.ResourceOrTagKey.Serializer.REGISTRY);
register(id("minecraft:resource", mapSet(MINECRAFT_1_20_5, 45), mapSet(MINECRAFT_1_20_3, 44), mapSet(MINECRAFT_1_19_3, 43),
mapSet(MINECRAFT_1_19, 44)),
register(id("minecraft:resource", mapSet(MINECRAFT_1_21_6, 46), mapSet(MINECRAFT_1_20_5, 45), mapSet(MINECRAFT_1_20_3, 44),
mapSet(MINECRAFT_1_19_3, 43), mapSet(MINECRAFT_1_19, 44)),
RegistryKeyArgument.class, RegistryKeyArgumentSerializer.REGISTRY);
register(id("minecraft:resource_key", mapSet(MINECRAFT_1_20_5, 46), mapSet(MINECRAFT_1_20_3, 45), mapSet(MINECRAFT_1_19_3, 44)),
register(id("minecraft:resource_key", mapSet(MINECRAFT_1_21_6, 47), mapSet(MINECRAFT_1_20_5, 46), mapSet(MINECRAFT_1_20_3, 45),
mapSet(MINECRAFT_1_19_3, 44)),
RegistryKeyArgumentList.ResourceKey.class,
RegistryKeyArgumentList.ResourceKey.Serializer.REGISTRY);
register(id("minecraft:resource_selector", mapSet(MINECRAFT_1_21_5, 47)),
register(id("minecraft:resource_selector", mapSet(MINECRAFT_1_21_6, 48), mapSet(MINECRAFT_1_21_5, 47)),
RegistryKeyArgumentList.ResourceSelector.class,
RegistryKeyArgumentList.ResourceSelector.Serializer.REGISTRY);
empty(id("minecraft:template_mirror", mapSet(MINECRAFT_1_21_5, 48), mapSet(MINECRAFT_1_20_5, 47), mapSet(MINECRAFT_1_20_3, 46), mapSet(MINECRAFT_1_19, 45))); // 1.19
empty(id("minecraft:template_rotation", mapSet(MINECRAFT_1_21_5, 49), mapSet(MINECRAFT_1_20_5, 48), mapSet(MINECRAFT_1_20_3, 47), mapSet(MINECRAFT_1_19, 46))); // 1.19
empty(id("minecraft:heightmap", mapSet(MINECRAFT_1_21_5, 50), mapSet(MINECRAFT_1_20_3, 49), mapSet(MINECRAFT_1_19_4, 47))); // 1.19.4
empty(id("minecraft:template_mirror", mapSet(MINECRAFT_1_21_6, 49), mapSet(MINECRAFT_1_21_5, 48), mapSet(MINECRAFT_1_20_5, 47),
mapSet(MINECRAFT_1_20_3, 46), mapSet(MINECRAFT_1_19, 45))); // 1.19
empty(id("minecraft:template_rotation", mapSet(MINECRAFT_1_21_6, 50), mapSet(MINECRAFT_1_21_5, 49), mapSet(MINECRAFT_1_20_5, 48),
mapSet(MINECRAFT_1_20_3, 47), mapSet(MINECRAFT_1_19, 46))); // 1.19
empty(id("minecraft:heightmap", mapSet(MINECRAFT_1_21_6, 51), mapSet(MINECRAFT_1_21_5, 50), mapSet(MINECRAFT_1_20_3, 49),
mapSet(MINECRAFT_1_19_4, 47))); // 1.19.4
empty(id("minecraft:uuid", mapSet(MINECRAFT_1_21_5, 54),mapSet(MINECRAFT_1_20_5, 53), mapSet(MINECRAFT_1_20_3, 48), mapSet(MINECRAFT_1_19_4, 48),
mapSet(MINECRAFT_1_19, 47))); // added in 1.16
empty(id("minecraft:uuid", mapSet(MINECRAFT_1_21_6, 56), mapSet(MINECRAFT_1_21_5, 54),mapSet(MINECRAFT_1_20_5, 53), mapSet(MINECRAFT_1_20_3, 48),
mapSet(MINECRAFT_1_19_4, 48), mapSet(MINECRAFT_1_19, 47))); // added in 1.16
empty(id("minecraft:loot_table", mapSet(MINECRAFT_1_21_5, 51), mapSet(MINECRAFT_1_20_5, 50)));
empty(id("minecraft:loot_predicate", mapSet(MINECRAFT_1_21_5, 52), mapSet(MINECRAFT_1_20_5, 51)));
empty(id("minecraft:loot_modifier", mapSet(MINECRAFT_1_21_5, 53), mapSet(MINECRAFT_1_20_5, 52)));
empty(id("minecraft:loot_table", mapSet(MINECRAFT_1_21_6, 52), mapSet(MINECRAFT_1_21_5, 51), mapSet(MINECRAFT_1_20_5, 50)));
empty(id("minecraft:loot_predicate", mapSet(MINECRAFT_1_21_6, 53), mapSet(MINECRAFT_1_21_5, 52), mapSet(MINECRAFT_1_20_5, 51)));
empty(id("minecraft:loot_modifier", mapSet(MINECRAFT_1_21_6, 54), mapSet(MINECRAFT_1_21_5, 53), mapSet(MINECRAFT_1_20_5, 52)));
empty(id("minecraft:hex_color", mapSet(MINECRAFT_1_21_6, 17))); // added in 1.21.6
empty(id("minecraft:dialog", mapSet(MINECRAFT_1_21_6, 55))); // added in 1.21.6
// Crossstitch support
register(id("crossstitch:mod_argument", mapSet(MINECRAFT_1_19, -256)), ModArgumentProperty.class, MOD);

View File

@ -116,6 +116,8 @@ public class KeyedPlayerChatPacket implements MinecraftPacket {
ProtocolUtils.readByteArray(buf));
}
}
unsigned = true;
}
@Override

View File

@ -132,6 +132,7 @@ public class KeyedPlayerCommandPacket implements MinecraftPacket {
unsigned = true;
}
unsigned = true;
}
@Override

View File

@ -69,6 +69,7 @@ public class SessionPlayerChatPacket implements MinecraftPacket {
this.salt = buf.readLong();
this.signed = buf.readBoolean();
if (this.signed) {
this.signed = false;
this.signature = readMessageSignature(buf);
} else {
this.signature = new byte[0];

View File

@ -46,6 +46,8 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
this.salt = buf.readLong();
this.argumentSignatures = new ArgumentSignatures(buf);
this.lastSeenMessages = new LastSeenMessages(buf, protocolVersion);
this.argumentSignatures = new ArgumentSignatures();
}
@Override

View File

@ -36,7 +36,7 @@ public class KnownPacksPacket implements MinecraftPacket {
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
final int packCount = ProtocolUtils.readVarInt(buf);
if (packCount > MAX_LENGTH_PACKS) {
if (direction == ProtocolUtils.Direction.SERVERBOUND && packCount > MAX_LENGTH_PACKS) {
throw TOO_MANY_PACKS;
}

View File

@ -114,7 +114,7 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud
server.createBootstrap(loop).handler(new ChannelInitializer<>() {
@Override
protected void initChannel(Channel ch) {
ch.pipeline().addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
ch.pipeline().addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder(ProtocolUtils.Direction.CLIENTBOUND))
.addLast(READ_TIMEOUT, new ReadTimeoutHandler(
pingOptions.getTimeout() == 0
? server.getConfiguration().getReadTimeout()

View File

@ -24,9 +24,6 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import net.kyori.adventure.translation.GlobalTranslator;
import net.kyori.adventure.translation.TranslationRegistry;
import net.kyori.adventure.translation.Translator;
import org.jetbrains.annotations.Nullable;
/**
* Velocity Translation Mapper.
@ -43,25 +40,9 @@ public enum TranslatableMapper implements BiConsumer<TranslatableComponent, Cons
final TranslatableComponent translatableComponent,
final Consumer<Component> componentConsumer
) {
for (final Translator source : GlobalTranslator.translator().sources()) {
if (source instanceof TranslationRegistry registry
&& registry.contains(translatableComponent.key())) {
componentConsumer.accept(GlobalTranslator.render(translatableComponent,
ClosestLocaleMatcher.INSTANCE.lookupClosest(Locale.getDefault())));
return;
}
}
final @Nullable String fallback = translatableComponent.fallback();
if (fallback == null) {
return;
}
for (final Translator source : GlobalTranslator.translator().sources()) {
if (source instanceof TranslationRegistry registry && registry.contains(fallback)) {
componentConsumer.accept(
GlobalTranslator.render(Component.translatable(fallback),
ClosestLocaleMatcher.INSTANCE.lookupClosest(Locale.getDefault())));
return;
}
final Locale locale = ClosestLocaleMatcher.INSTANCE.lookupClosest(Locale.getDefault());
if (GlobalTranslator.translator().canTranslate(translatableComponent.key(), locale)) {
componentConsumer.accept(GlobalTranslator.render(translatableComponent, locale));
}
}
}

View File

@ -35,7 +35,7 @@ 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.player-not-found=Играч с името {0} не съществува.
velocity.command.server-current-server=В момента сте свързан към {0}.
velocity.command.server-too-many=Има прекалено много регистрирани сървъри. Използвайте TAB, за да видите всички налични сървъри.
velocity.command.server-available=Налични сървъри\:

View File

@ -29,7 +29,7 @@ velocity.error.modern-forwarding-needs-new-client=Este servidor solo es compatib
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.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=Illegal characters in chat
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.
@ -60,6 +60,6 @@ velocity.command.dump-success=Se ha creado un informe anónimo que contiene info
velocity.command.dump-will-expire=Este enlace caducará en unos días.
velocity.command.dump-server-error=Se ha producido un error en los servidores de Velocity y la subida no se ha podido completar. Notifica al equipo de Velocity sobre este problema y proporciona los detalles sobre este error disponibles en el archivo de registro o la consola de tu servidor Velocity.
velocity.command.dump-offline=Causa probable\: la configuración DNS del sistema no es válida o no hay conexión a internet
velocity.command.send-usage=/send <player> <server>
velocity.command.send-usage=/send <jugador> <servidor>
# Kick
velocity.kick.shutdown=Proxy shutting down.
velocity.kick.shutdown=El proxy se ha apagado.

9
steamwarci.yml Normal file
View File

@ -0,0 +1,9 @@
build:
- "./gradlew build -x check -x javadoc --no-daemon"
artifacts:
"/jars/Velocity.jar": "proxy/build/libs/velocity-proxy-3.4.0-SNAPSHOT-all.jar"
release:
- "mvn deploy:deploy-file -DgroupId=de.steamwar -DartifactId=velocity -Dversion=RELEASE -Dpackaging=jar -Dfile=/jars/Velocity.jar -Durl=file:///var/www/maven/"