From 2b6c271cc778c0d6876e113f5229097ad22884ad Mon Sep 17 00:00:00 2001 From: Frank van der Heijden Date: Sun, 13 Sep 2020 19:11:40 +0200 Subject: [PATCH 01/39] Add ability to check for command alias existence in api module --- .../com/velocitypowered/api/command/CommandManager.java | 8 ++++++++ .../proxy/command/VelocityCommandManager.java | 1 + 2 files changed, 9 insertions(+) diff --git a/api/src/main/java/com/velocitypowered/api/command/CommandManager.java b/api/src/main/java/com/velocitypowered/api/command/CommandManager.java index b688adc63..68c3ed2c8 100644 --- a/api/src/main/java/com/velocitypowered/api/command/CommandManager.java +++ b/api/src/main/java/com/velocitypowered/api/command/CommandManager.java @@ -122,4 +122,12 @@ public interface CommandManager { * Can be completed exceptionally if an exception is thrown during execution. */ CompletableFuture executeImmediatelyAsync(CommandSource source, String cmdLine); + + /** + * Returns whether the given alias is registered on this manager. + * + * @param alias the command alias to check + * @return {@code true} if the alias is registered + */ + boolean hasCommand(String alias); } 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 cd5f034d7..b2d4e4f27 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -227,6 +227,7 @@ public class VelocityCommandManager implements CommandManager { * @param alias the command alias to check * @return {@code true} if the alias is registered */ + @Override public boolean hasCommand(final String alias) { Preconditions.checkNotNull(alias, "alias"); return dispatcher.getRoot().getChild(alias.toLowerCase(Locale.ENGLISH)) != null; From fae1ee83733c9d483b431982826b1e0d892b4b42 Mon Sep 17 00:00:00 2001 From: derklaro Date: Tue, 15 Sep 2020 13:27:16 +0200 Subject: [PATCH 02/39] Track some dependency updates junit: 5.3.0-M1 -> 5.7.0 slf4j: 1.7.25 -> 1.7.30 netty: 4.1.51.Final -> 4.1.52.Final guava: 25.1-jre -> 29.0-jre checkerFramework: 2.7.0 -> 3.6.1 jline-terminal-jansi: 3.12.1 -> 3.16.0 fastutil: 8.2.3 -> 8.4.1 async-http-client: 2.10.4 -> 2.12.1 completable-futures: 0.3.2 -> 0.3.3 gradle-wrapper: 6.6 -> 6.6.1 BREAKING CHANGE: No breaking change --- build.gradle | 10 +++++----- gradle/wrapper/gradle-wrapper.properties | 2 +- proxy/build.gradle | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 4d6810e9c..981dd9b90 100644 --- a/build.gradle +++ b/build.gradle @@ -22,12 +22,12 @@ allprojects { // dependency versions textVersion = '3.0.4' adventureVersion = '4.0.0-SNAPSHOT' - junitVersion = '5.3.0-M1' - slf4jVersion = '1.7.25' + junitVersion = '5.7.0' + slf4jVersion = '1.7.30' log4jVersion = '2.13.3' - nettyVersion = '4.1.51.Final' - guavaVersion = '25.1-jre' - checkerFrameworkVersion = '2.7.0' + nettyVersion = '4.1.52.Final' + guavaVersion = '29.0-jre' + checkerFrameworkVersion = '3.6.1' configurateVersion = '3.7.1' getCurrentShortRevision = { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 01be9d5c3..71796f880 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip diff --git a/proxy/build.gradle b/proxy/build.gradle index 88175336f..e7eb88a84 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -56,16 +56,16 @@ dependencies { implementation 'net.sf.jopt-simple:jopt-simple:5.0.4' // command-line options implementation 'net.minecrell:terminalconsoleappender:1.2.0' - runtimeOnly 'org.jline:jline-terminal-jansi:3.12.1' // Needed for JLine + runtimeOnly 'org.jline:jline-terminal-jansi:3.16.0' // Needed for JLine runtimeOnly 'com.lmax:disruptor:3.4.2' // Async loggers - implementation 'it.unimi.dsi:fastutil:8.2.3' + implementation 'it.unimi.dsi:fastutil:8.4.1' implementation 'net.kyori:event-method-asm:4.0.0-SNAPSHOT' implementation 'net.kyori:adventure-nbt:4.0.0-SNAPSHOT' - implementation 'org.asynchttpclient:async-http-client:2.10.4' + implementation 'org.asynchttpclient:async-http-client:2.12.1' - implementation 'com.spotify:completable-futures:0.3.2' + implementation 'com.spotify:completable-futures:0.3.3' implementation 'com.electronwill.night-config:toml:3.6.3' From 708094b03a67b3cf3c2dad4a9d2c83037489bdd6 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 19 Sep 2020 18:09:05 -0400 Subject: [PATCH 03/39] Revert Guava bump as it is a breaking change --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 981dd9b90..1f41da8da 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ allprojects { slf4jVersion = '1.7.30' log4jVersion = '2.13.3' nettyVersion = '4.1.52.Final' - guavaVersion = '29.0-jre' + guavaVersion = '25.1-jre' checkerFrameworkVersion = '3.6.1' configurateVersion = '3.7.1' From b61321f4f74cad16447bf02ace9707d7405bb7bb Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 19 Sep 2020 16:47:55 -0400 Subject: [PATCH 04/39] Shorten name of DurationUtils#toTicks --- .../proxy/connection/client/ConnectedPlayer.java | 6 +++--- .../java/com/velocitypowered/proxy/util/DurationUtils.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) 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 c97e492c4..f6ed9856b 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 @@ -311,9 +311,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { TitlePacket timesPkt = TitlePacket.timesForProtocolVersion(this.getProtocolVersion()); net.kyori.adventure.title.Title.Times times = title.times(); if (times != null) { - timesPkt.setFadeIn((int) DurationUtils.convertDurationToTicks(times.fadeIn())); - timesPkt.setStay((int) DurationUtils.convertDurationToTicks(times.stay())); - timesPkt.setFadeOut((int) DurationUtils.convertDurationToTicks(times.fadeOut())); + timesPkt.setFadeIn((int) DurationUtils.toTicks(times.fadeIn())); + timesPkt.setStay((int) DurationUtils.toTicks(times.stay())); + timesPkt.setFadeOut((int) DurationUtils.toTicks(times.fadeOut())); } connection.delayedWrite(timesPkt); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/DurationUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/util/DurationUtils.java index 4742613e9..a588ed222 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/DurationUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/DurationUtils.java @@ -18,7 +18,7 @@ public final class DurationUtils { * @param duration the duration to convert into Minecraft ticks * @return the duration represented as the number of Minecraft ticks */ - public static long convertDurationToTicks(Duration duration) { + public static long toTicks(Duration duration) { return duration.toMillis() / ONE_TICK_IN_MILLISECONDS; } } From 6c348c994cbae216914693c7ee3fba7551548749 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 21 Sep 2020 11:27:43 -0400 Subject: [PATCH 05/39] Don't need to call memoryAddress() here as we do it later on anyway --- .../natives/compression/LibdeflateVelocityCompressor.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java b/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java index e748fd70b..19dead4ac 100644 --- a/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java +++ b/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java @@ -29,8 +29,6 @@ public class LibdeflateVelocityCompressor implements VelocityCompressor { public void inflate(ByteBuf source, ByteBuf destination, int uncompressedSize) throws DataFormatException { ensureNotDisposed(); - source.memoryAddress(); - destination.memoryAddress(); // libdeflate recommends we work with a known uncompressed size - so we work strictly within // those parameters. If the uncompressed size doesn't match the compressed size, then we will From a0a0966f99bff672b9a28d55b464a4ffd5ee4d2e Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 23 Sep 2020 01:02:19 -0400 Subject: [PATCH 06/39] Minor code cleanup --- .../com/velocitypowered/proxy/protocol/ProtocolUtils.java | 6 +----- .../proxy/protocol/util/ByteBufDataOutput.java | 2 +- .../java/com/velocitypowered/proxy/util/BrigadierUtils.java | 3 --- .../proxy/util/VelocityChannelRegistrar.java | 3 +-- 4 files changed, 3 insertions(+), 11 deletions(-) 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 2fa1e759a..a7e0bd437 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -271,10 +271,6 @@ public enum ProtocolUtils { * @param stringArray the array to write */ public static void writeStringArray(ByteBuf buf, String[] stringArray) { - if (stringArray == null) { - writeVarInt(buf, 0); - return; - } writeVarInt(buf, stringArray.length); for (String s : stringArray) { writeString(buf, s); @@ -292,7 +288,7 @@ public enum ProtocolUtils { writeString(buf, property.getName()); writeString(buf, property.getValue()); String signature = property.getSignature(); - if (signature != null) { + if (signature != null && !signature.isEmpty()) { buf.writeBoolean(true); writeString(buf, signature); } else { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java index ff8149332..644c1e3c6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java @@ -10,7 +10,7 @@ import java.io.OutputStream; import java.nio.charset.StandardCharsets; /** - * A {@link DataOutput} equivalent to {@link ByteBufDataInput}. + * A {@link ByteArrayDataOutput} equivalent to {@link ByteBufDataInput}. */ public class ByteBufDataOutput extends OutputStream implements ByteArrayDataOutput { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/BrigadierUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/util/BrigadierUtils.java index 5d753c155..1bad01978 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/BrigadierUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/BrigadierUtils.java @@ -1,10 +1,8 @@ package com.velocitypowered.proxy.util; -import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.mojang.brigadier.Command; import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.context.CommandContext; @@ -13,7 +11,6 @@ import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; import com.velocitypowered.api.command.CommandSource; import java.util.Locale; -import org.checkerframework.checker.nullness.qual.Nullable; /** * Provides utilities for working with Brigadier commands. diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityChannelRegistrar.java b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityChannelRegistrar.java index bb7c383bf..b07e4815f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityChannelRegistrar.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityChannelRegistrar.java @@ -21,8 +21,7 @@ public class VelocityChannelRegistrar implements ChannelRegistrar { public void register(ChannelIdentifier... identifiers) { for (ChannelIdentifier identifier : identifiers) { Preconditions.checkArgument(identifier instanceof LegacyChannelIdentifier - || identifier instanceof MinecraftChannelIdentifier, - "identifier is unknown"); + || identifier instanceof MinecraftChannelIdentifier, "identifier is unknown"); } for (ChannelIdentifier identifier : identifiers) { From dc48eb97f98d83e44d7967d934576c785eb60e65 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 23 Sep 2020 01:03:18 -0400 Subject: [PATCH 07/39] Start publishing velocity-natives module Note that this will remain internal API for Velocity and the publication of the module does not necessarily indicate that they will be stable. --- api/build.gradle | 1 + gradle/publish.gradle | 16 ++++++++++++++++ native/build.gradle | 2 ++ 3 files changed, 19 insertions(+) create mode 100644 gradle/publish.gradle diff --git a/api/build.gradle b/api/build.gradle index 4a275b55f..25142d71a 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -5,6 +5,7 @@ plugins { } apply from: '../gradle/checkstyle.gradle' +apply from: '../gradle/publish.gradle' apply plugin: 'com.github.johnrengelman.shadow' sourceSets { diff --git a/gradle/publish.gradle b/gradle/publish.gradle new file mode 100644 index 000000000..cb94e0aa1 --- /dev/null +++ b/gradle/publish.gradle @@ -0,0 +1,16 @@ +publishing { + repositories { + maven { + credentials { + username System.getenv("NEXUS_USERNAME") + password System.getenv("NEXUS_PASSWORD") + } + + name = 'velocity-nexus' + def base = 'https://nexus.velocitypowered.com/repository/velocity-artifacts' + def releasesRepoUrl = "$base-releases/" + def snapshotsRepoUrl = "$base-snapshots/" + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + } + } +} \ No newline at end of file diff --git a/native/build.gradle b/native/build.gradle index 72ed54609..9d0ec17f6 100644 --- a/native/build.gradle +++ b/native/build.gradle @@ -1,9 +1,11 @@ plugins { id 'java-library' id 'checkstyle' + id 'maven-publish' } apply from: '../gradle/checkstyle.gradle' +apply from: '../gradle/publish.gradle' dependencies { implementation "com.google.guava:guava:${guavaVersion}" From d2b65cb643bb174afade9690b093f847e28d2653 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 23 Sep 2020 01:05:50 -0400 Subject: [PATCH 08/39] Fix velocity-natives module publishing, take 1 --- native/build.gradle | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/native/build.gradle b/native/build.gradle index 9d0ec17f6..ed7cf0aa3 100644 --- a/native/build.gradle +++ b/native/build.gradle @@ -14,4 +14,12 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}" testImplementation "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } } \ No newline at end of file From cc6546bea9783eb5a1672e2c936ccf6ab819e5d8 Mon Sep 17 00:00:00 2001 From: Riley Park Date: Mon, 28 Sep 2020 05:30:33 -0700 Subject: [PATCH 09/39] Update for Adventure changes --- .../velocitypowered/proxy/VelocityServer.java | 8 +-- .../proxy/command/VelocityCommandManager.java | 3 +- .../proxy/command/builtin/GlistCommand.java | 24 +++++---- .../proxy/command/builtin/ServerCommand.java | 19 +++---- .../command/builtin/VelocityCommand.java | 51 ++++++++++--------- .../backend/LoginSessionHandler.java | 5 +- .../client/ClientPlaySessionHandler.java | 4 +- .../connection/client/ConnectedPlayer.java | 6 +-- .../client/HandshakeSessionHandler.java | 9 ++-- .../connection/util/ConnectionMessages.java | 13 ++--- .../VelocityLegacyHoverEventSerializer.java | 12 ++--- 11 files changed, 81 insertions(+), 73 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index a641acfb1..6fa6223ae 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -344,14 +344,14 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { player.createConnectionRequest(next.get()).connectWithIndication() .whenComplete((success, ex) -> { if (ex != null || success == null || !success) { - player.disconnect(TextComponent.of("Your server has been changed, but we could " + player.disconnect(Component.text("Your server has been changed, but we could " + "not move you to any fallback servers.")); } latch.countDown(); }); } else { latch.countDown(); - player.disconnect(TextComponent.of("Your server has been changed, but we could " + player.disconnect(Component.text("Your server has been changed, but we could " + "not move you to any fallback servers.")); } } @@ -466,7 +466,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { * @param explicitExit whether the user explicitly shut down the proxy */ public void shutdown(boolean explicitExit) { - shutdown(explicitExit, TextComponent.of("Proxy shutting down.")); + shutdown(explicitExit, Component.text("Proxy shutting down.")); } @Override @@ -520,7 +520,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { } else { ConnectedPlayer existing = connectionsByUuid.get(connection.getUniqueId()); if (existing != null) { - existing.disconnect(TranslatableComponent.of("multiplayer.disconnect.duplicate_login")); + existing.disconnect(Component.translatable("multiplayer.disconnect.duplicate_login")); } // We can now replace the entries as needed. 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 b2d4e4f27..491a14aff 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -24,6 +24,7 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.concurrent.CompletableFuture; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; @@ -161,7 +162,7 @@ public class VelocityCommandManager implements CommandManager { boolean isSyntaxError = !e.getType().equals( CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()); if (isSyntaxError) { - source.sendMessage(TextComponent.of(e.getMessage(), NamedTextColor.RED)); + source.sendMessage(Component.text(e.getMessage(), NamedTextColor.RED)); // This is, of course, a lie, but the API will need to change... return true; } else { 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 207ee028f..9337d4ea7 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 @@ -17,6 +17,7 @@ import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.server.RegisteredServer; import java.util.List; import java.util.Optional; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; @@ -59,9 +60,10 @@ public class GlistCommand { final CommandSource source = context.getSource(); sendTotalProxyCount(source); source.sendMessage( - TextComponent.builder("To view all players on servers, use ", NamedTextColor.YELLOW) - .append("/glist all", NamedTextColor.DARK_AQUA) - .append(".", NamedTextColor.YELLOW) + Component.text().content("To view all players on servers, use ") + .color(NamedTextColor.YELLOW) + .append(Component.text("/glist all", NamedTextColor.DARK_AQUA)) + .append(Component.text(".", NamedTextColor.YELLOW)) .build()); return 1; } @@ -78,7 +80,7 @@ public class GlistCommand { Optional registeredServer = server.getServer(serverName); if (!registeredServer.isPresent()) { source.sendMessage( - TextComponent.of("Server " + serverName + " doesn't exist.", NamedTextColor.RED)); + Component.text("Server " + serverName + " doesn't exist.", NamedTextColor.RED)); return -1; } sendServerPlayers(source, registeredServer.get(), false); @@ -87,9 +89,9 @@ public class GlistCommand { } private void sendTotalProxyCount(CommandSource target) { - target.sendMessage(TextComponent.builder("There are ", NamedTextColor.YELLOW) - .append(Integer.toString(server.getAllPlayers().size()), NamedTextColor.GREEN) - .append(" player(s) online.", NamedTextColor.YELLOW) + target.sendMessage(Component.text().content("There are ").color(NamedTextColor.YELLOW) + .append(Component.text(server.getAllPlayers().size(), NamedTextColor.GREEN)) + .append(Component.text(" player(s) online.", NamedTextColor.YELLOW)) .build()); } @@ -99,11 +101,11 @@ public class GlistCommand { return; } - TextComponent.Builder builder = TextComponent.builder() + TextComponent.Builder builder = Component.text() .append(TextComponent.of("[" + server.getServerInfo().getName() + "] ", NamedTextColor.DARK_AQUA)) - .append("(" + onServer.size() + ")", NamedTextColor.GRAY) - .append(": ") + .append(Component.text("(" + onServer.size() + ")", NamedTextColor.GRAY)) + .append(Component.text(": ")) .resetStyle(); for (int i = 0; i < onServer.size(); i++) { @@ -111,7 +113,7 @@ public class GlistCommand { builder.append(player.getUsername()); if (i + 1 < onServer.size()) { - builder.append(", "); + builder.append(Component.text(", ")); } } 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 afc9551ed..4c606902b 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 @@ -15,6 +15,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; @@ -34,7 +35,7 @@ public class ServerCommand implements SimpleCommand { final String[] args = invocation.arguments(); if (!(source instanceof Player)) { - source.sendMessage(TextComponent.of("Only players may run this command.", + source.sendMessage(Component.text("Only players may run this command.", NamedTextColor.RED)); return; } @@ -46,7 +47,7 @@ public class ServerCommand implements SimpleCommand { Optional toConnect = server.getServer(serverName); if (!toConnect.isPresent()) { player.sendMessage( - TextComponent.of("Server " + serverName + " doesn't exist.", NamedTextColor.RED)); + Component.text("Server " + serverName + " doesn't exist.", NamedTextColor.RED)); return; } @@ -59,24 +60,24 @@ public class ServerCommand implements SimpleCommand { private void outputServerInformation(Player executor) { String currentServer = executor.getCurrentServer().map(ServerConnection::getServerInfo) .map(ServerInfo::getName).orElse(""); - executor.sendMessage(TextComponent.of("You are currently connected to " + currentServer + ".", + executor.sendMessage(Component.text("You are currently connected to " + currentServer + ".", NamedTextColor.YELLOW)); List servers = BuiltinCommandUtil.sortedServerList(server); if (servers.size() > MAX_SERVERS_TO_LIST) { - executor.sendMessage(TextComponent.of( + executor.sendMessage(Component.text( "Too many servers to list. Tab-complete to show all servers.", NamedTextColor.RED)); return; } // Assemble the list of servers as components - TextComponent.Builder serverListBuilder = TextComponent.builder("Available servers: ") + TextComponent.Builder serverListBuilder = Component.text().content("Available servers: ") .color(NamedTextColor.YELLOW); for (int i = 0; i < servers.size(); i++) { RegisteredServer rs = servers.get(i); serverListBuilder.append(formatServerComponent(currentServer, rs)); if (i != servers.size() - 1) { - serverListBuilder.append(TextComponent.of(", ", NamedTextColor.GRAY)); + serverListBuilder.append(Component.text(", ", NamedTextColor.GRAY)); } } @@ -85,19 +86,19 @@ public class ServerCommand implements SimpleCommand { private TextComponent formatServerComponent(String currentPlayerServer, RegisteredServer server) { ServerInfo serverInfo = server.getServerInfo(); - TextComponent serverTextComponent = TextComponent.of(serverInfo.getName()); + TextComponent serverTextComponent = Component.text(serverInfo.getName()); String playersText = server.getPlayersConnected().size() + " player(s) online"; if (serverInfo.getName().equals(currentPlayerServer)) { serverTextComponent = serverTextComponent.color(NamedTextColor.GREEN) .hoverEvent( - showText(TextComponent.of("Currently connected to this server\n" + playersText)) + showText(Component.text("Currently connected to this server\n" + playersText)) ); } else { serverTextComponent = serverTextComponent.color(NamedTextColor.GRAY) .clickEvent(ClickEvent.runCommand("/server " + serverInfo.getName())) .hoverEvent( - showText(TextComponent.of("Click to connect to this server\n" + playersText)) + showText(Component.text("Click to connect to this server\n" + playersText)) ); } return serverTextComponent; 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 1a0466f58..47cc3fb93 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 @@ -16,6 +16,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.HoverEvent; @@ -59,7 +60,7 @@ public class VelocityCommand implements SimpleCommand { .map(Map.Entry::getKey) .collect(Collectors.joining("|")); String commandText = "/velocity <" + availableCommands + ">"; - source.sendMessage(TextComponent.of(commandText, NamedTextColor.RED)); + source.sendMessage(Component.text(commandText, NamedTextColor.RED)); } @Override @@ -142,15 +143,15 @@ public class VelocityCommand implements SimpleCommand { public void execute(CommandSource source, String @NonNull [] args) { try { if (server.reloadConfiguration()) { - source.sendMessage(TextComponent.of("Configuration reloaded.", NamedTextColor.GREEN)); + source.sendMessage(Component.text("Configuration reloaded.", NamedTextColor.GREEN)); } else { - source.sendMessage(TextComponent.of( + source.sendMessage(Component.text( "Unable to reload your configuration. Check the console for more details.", NamedTextColor.RED)); } } catch (Exception e) { logger.error("Unable to reload configuration", e); - source.sendMessage(TextComponent.of( + source.sendMessage(Component.text( "Unable to reload your configuration. Check the console for more details.", NamedTextColor.RED)); } @@ -173,16 +174,16 @@ public class VelocityCommand implements SimpleCommand { @Override public void execute(CommandSource source, String @NonNull [] args) { if (args.length != 0) { - source.sendMessage(TextComponent.of("/velocity version", NamedTextColor.RED)); + source.sendMessage(Component.text("/velocity version", NamedTextColor.RED)); return; } ProxyVersion version = server.getVersion(); - TextComponent velocity = TextComponent.builder(version.getName() + " ") + TextComponent velocity = Component.text().content(version.getName() + " ") .decoration(TextDecoration.BOLD, true) .color(NamedTextColor.DARK_AQUA) - .append(TextComponent.of(version.getVersion()).decoration(TextDecoration.BOLD, false)) + .append(Component.text(version.getVersion()).decoration(TextDecoration.BOLD, false)) .build(); TextComponent copyright = TextComponent .of("Copyright 2018-2020 " + version.getVendor() + ". " + version.getName() @@ -191,15 +192,15 @@ public class VelocityCommand implements SimpleCommand { source.sendMessage(copyright); if (version.getName().equals("Velocity")) { - TextComponent velocityWebsite = TextComponent.builder() + TextComponent velocityWebsite = Component.text() .content("Visit the ") - .append(TextComponent.builder("Velocity website") + .append(Component.text().content("Velocity website") .color(NamedTextColor.GREEN) .clickEvent( ClickEvent.openUrl("https://www.velocitypowered.com")) .build()) - .append(TextComponent.of(" or the ")) - .append(TextComponent.builder("Velocity GitHub") + .append(Component.text(" or the ")) + .append(Component.text().content("Velocity GitHub") .color(NamedTextColor.GREEN) .clickEvent(ClickEvent.openUrl( "https://github.com/VelocityPowered/Velocity")) @@ -226,7 +227,7 @@ public class VelocityCommand implements SimpleCommand { @Override public void execute(CommandSource source, String @NonNull [] args) { if (args.length != 0) { - source.sendMessage(TextComponent.of("/velocity plugins", NamedTextColor.RED)); + source.sendMessage(Component.text("/velocity plugins", NamedTextColor.RED)); return; } @@ -234,17 +235,17 @@ public class VelocityCommand implements SimpleCommand { int pluginCount = plugins.size(); if (pluginCount == 0) { - source.sendMessage(TextComponent.of("No plugins installed.", NamedTextColor.YELLOW)); + source.sendMessage(Component.text("No plugins installed.", NamedTextColor.YELLOW)); return; } - TextComponent.Builder output = TextComponent.builder("Plugins: ") + TextComponent.Builder output = Component.text().content("Plugins: ") .color(NamedTextColor.YELLOW); for (int i = 0; i < pluginCount; i++) { PluginContainer plugin = plugins.get(i); output.append(componentForPlugin(plugin.getDescription())); if (i + 1 < pluginCount) { - output.append(TextComponent.of(", ")); + output.append(Component.text(", ")); } } @@ -255,28 +256,28 @@ public class VelocityCommand implements SimpleCommand { String pluginInfo = description.getName().orElse(description.getId()) + description.getVersion().map(v -> " " + v).orElse(""); - TextComponent.Builder hoverText = TextComponent.builder(pluginInfo); + TextComponent.Builder hoverText = Component.text().content(pluginInfo); description.getUrl().ifPresent(url -> { - hoverText.append(TextComponent.newline()); - hoverText.append(TextComponent.of("Website: " + url)); + hoverText.append(Component.newline()); + hoverText.append(Component.text("Website: " + url)); }); if (!description.getAuthors().isEmpty()) { - hoverText.append(TextComponent.newline()); + hoverText.append(Component.newline()); if (description.getAuthors().size() == 1) { - hoverText.append(TextComponent.of("Author: " + description.getAuthors().get(0))); + hoverText.append(Component.text("Author: " + description.getAuthors().get(0))); } else { - hoverText.append(TextComponent.of("Authors: " + Joiner.on(", ") + hoverText.append(Component.text("Authors: " + Joiner.on(", ") .join(description.getAuthors()))); } } description.getDescription().ifPresent(pdesc -> { - hoverText.append(TextComponent.newline()); - hoverText.append(TextComponent.newline()); - hoverText.append(TextComponent.of(pdesc)); + hoverText.append(Component.newline()); + hoverText.append(Component.newline()); + hoverText.append(Component.text(pdesc)); }); - return TextComponent.of(description.getId(), NamedTextColor.GRAY) + return Component.text(description.getId(), NamedTextColor.GRAY) .hoverEvent(HoverEvent.showText(hoverText.build())); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java index 15980b863..dc839640c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java @@ -26,12 +26,13 @@ import java.util.concurrent.CompletableFuture; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; public class LoginSessionHandler implements MinecraftSessionHandler { - private static final TextComponent MODERN_IP_FORWARDING_FAILURE = TextComponent - .of("Your server did not send a forwarding request to the proxy. Is it set up correctly?"); + private static final TextComponent MODERN_IP_FORWARDING_FAILURE = Component + .text("Your server did not send a forwarding request to the proxy. Is it set up correctly?"); private final VelocityServer server; private final VelocityServerConnection serverConn; 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 e44f83ce6..846b4cdbf 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 @@ -43,7 +43,7 @@ import java.util.Optional; import java.util.Queue; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -138,7 +138,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { logger.info("Exception occurred while running command for {}", player.getUsername(), e); player.sendMessage( - TextComponent.of("An error occurred while running this command.", + Component.text("An error occurred while running this command.", NamedTextColor.RED)); return null; }); 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 f6ed9856b..3ad998ef4 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 @@ -504,7 +504,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { userMessage = "Unable to connect to " + server.getServerInfo().getName() + ". Try again " + "later."; } - handleConnectionException(server, null, TextComponent.of(userMessage, + handleConnectionException(server, null, Component.text(userMessage, NamedTextColor.RED), safe); } @@ -527,7 +527,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) { logger.error("{}: kicked from server {}: {}", this, server.getServerInfo().getName(), plainTextReason); - handleConnectionException(server, disconnectReason, TextComponent.builder() + handleConnectionException(server, disconnectReason, Component.text() .append(messages.getKickPrefix(server.getServerInfo().getName())) .color(NamedTextColor.RED) .append(disconnectReason) @@ -535,7 +535,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } else { logger.error("{}: disconnected while connecting to {}: {}", this, server.getServerInfo().getName(), plainTextReason); - handleConnectionException(server, disconnectReason, TextComponent.builder() + handleConnectionException(server, disconnectReason, Component.text() .append(messages.getDisconnectPrefix(server.getServerInfo().getName())) .color(NamedTextColor.RED) .append(disconnectReason) 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 83a25d4e8..632181582 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 @@ -22,6 +22,7 @@ import io.netty.buffer.ByteBuf; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Optional; +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; @@ -54,7 +55,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { @Override public boolean handle(LegacyHandshake packet) { connection.closeWith(LegacyDisconnect - .from(TextComponent.of("Your client is old, please upgrade!", NamedTextColor.RED))); + .from(Component.text("Your client is old, please upgrade!", NamedTextColor.RED))); return true; } @@ -100,13 +101,13 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { private void handleLogin(Handshake handshake, InitialInboundConnection ic) { if (!ProtocolVersion.isSupported(handshake.getProtocolVersion())) { - ic.disconnectQuietly(TranslatableComponent.of("multiplayer.disconnect.outdated_client")); + ic.disconnectQuietly(Component.translatable("multiplayer.disconnect.outdated_client")); return; } InetAddress address = ((InetSocketAddress) connection.getRemoteAddress()).getAddress(); if (!server.getIpAttemptLimiter().attempt(address)) { - ic.disconnectQuietly(TextComponent.of("You are logging in too fast, try again later.")); + ic.disconnectQuietly(Component.text("You are logging in too fast, try again later.")); return; } @@ -116,7 +117,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { // and lower, otherwise IP information will never get forwarded. if (server.getConfiguration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN && handshake.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_13) < 0) { - ic.disconnectQuietly(TextComponent.of("This server is only compatible with 1.13 and above.")); + ic.disconnectQuietly(Component.text("This server is only compatible with 1.13 and above.")); return; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionMessages.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionMessages.java index 86f248297..50c9f3e88 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionMessages.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionMessages.java @@ -1,16 +1,17 @@ package com.velocitypowered.proxy.connection.util; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; public class ConnectionMessages { - public static final TextComponent ALREADY_CONNECTED = TextComponent - .of("You are already connected to this server!", NamedTextColor.RED); - public static final TextComponent IN_PROGRESS = TextComponent - .of("You are already connecting to a server!", NamedTextColor.RED); - public static final TextComponent INTERNAL_SERVER_CONNECTION_ERROR = TextComponent - .of("An internal server connection error occurred.", NamedTextColor.RED); + public static final TextComponent ALREADY_CONNECTED = Component + .text("You are already connected to this server!", NamedTextColor.RED); + public static final TextComponent IN_PROGRESS = Component + .text("You are already connecting to a server!", NamedTextColor.RED); + public static final TextComponent INTERNAL_SERVER_CONNECTION_ERROR = Component + .text("An internal server connection error occurred.", NamedTextColor.RED); private ConnectionMessages() { throw new AssertionError(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/VelocityLegacyHoverEventSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/VelocityLegacyHoverEventSerializer.java index 02af541a6..dbf27b039 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/VelocityLegacyHoverEventSerializer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/VelocityLegacyHoverEventSerializer.java @@ -31,7 +31,7 @@ public class VelocityLegacyHoverEventSerializer implements LegacyHoverEventSeria } private static Key legacyIdToFakeKey(byte id) { - return Key.of("velocity", "legacy_hover/id_" + id); + return Key.key("velocity", "legacy_hover/id_" + id); } @Override @@ -45,7 +45,7 @@ public class VelocityLegacyHoverEventSerializer implements LegacyHoverEventSeria if (idIfString.isEmpty()) { key = legacyIdToFakeKey(item.getByte("id")); } else { - key = Key.of(idIfString); + key = Key.key(idIfString); } byte count = item.getByte("Count", (byte) 1); @@ -62,10 +62,10 @@ public class VelocityLegacyHoverEventSerializer implements LegacyHoverEventSeria try { name = componentDecoder.decode(item.getString("name")); } catch (Exception e) { - name = TextComponent.of(item.getString("name")); + name = Component.text(item.getString("name")); } - return ShowEntity.of(Key.of(item.getString("type")), + return ShowEntity.of(Key.key(item.getString("type")), UUID.fromString(item.getString("id")), name); } @@ -89,7 +89,7 @@ public class VelocityLegacyHoverEventSerializer implements LegacyHoverEventSeria builder.put("tag", TagStringIO.get().asCompound(nbt.string())); } - return TextComponent.of(TagStringIO.get().asString(builder.build())); + return Component.text(TagStringIO.get().asString(builder.build())); } @Override @@ -102,6 +102,6 @@ public class VelocityLegacyHoverEventSerializer implements LegacyHoverEventSeria if (name != null) { tag.putString("name", componentEncoder.encode(name)); } - return TextComponent.of(TagStringIO.get().asString(tag.build())); + return Component.text(TagStringIO.get().asString(tag.build())); } } From cb74210cd866574148b61485abdec8d4466f1da2 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 2 Oct 2020 00:23:01 -0400 Subject: [PATCH 10/39] Ensure synchronous shutdown if the user kills the process. --- .../java/com/velocitypowered/proxy/VelocityServer.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 6fa6223ae..54c446a88 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -456,8 +456,12 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { } }; - Thread thread = new Thread(shutdownProcess); - thread.start(); + if (explicitExit) { + Thread thread = new Thread(shutdownProcess); + thread.start(); + } else { + shutdownProcess.run(); + } } /** From 3bf252cf45f9581062d1f8adb6718051530cd4d6 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 4 Oct 2020 15:30:28 -0400 Subject: [PATCH 11/39] Start optimizing server list ping. --- native/compile-linux.sh | 4 +- .../LibdeflateVelocityCompressor.java | 17 ++++++ .../compression/VelocityCompressor.java | 5 ++ .../velocitypowered/proxy/VelocityServer.java | 20 ++++--- .../netty/MinecraftCompressEncoder.java | 10 +++- .../protocol/util/FaviconSerializer.java | 29 ----------- .../protocol/util/ping/FaviconSerializer.java | 26 ++++++++++ .../protocol/util/ping/PlayersSerializer.java | 45 ++++++++++++++++ .../util/ping/ServerPingSerializer.java | 52 +++++++++++++++++++ .../protocol/util/ping/VersionSerializer.java | 47 +++++++++++++++++ 10 files changed, 216 insertions(+), 39 deletions(-) delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/FaviconSerializer.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/PlayersSerializer.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/ServerPingSerializer.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/VersionSerializer.java diff --git a/native/compile-linux.sh b/native/compile-linux.sh index 6d92b6462..49c621af9 100755 --- a/native/compile-linux.sh +++ b/native/compile-linux.sh @@ -7,10 +7,10 @@ fi echo "Compiling libdeflate..." cd libdeflate || exit -CFLAGS="-fPIC -O2" make +CFLAGS="-fPIC -O2 -fomit-frame-pointer" make cd .. -CFLAGS="-O3 -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ -fPIC -shared -Wl,-z,noexecstack" +CFLAGS="-O2 -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ -fPIC -shared -Wl,-z,noexecstack -fomit-frame-pointer" ARCH=$(uname -m) mkdir -p src/main/resources/linux_$ARCH gcc $CFLAGS -Ilibdeflate src/main/c/jni_util.c src/main/c/jni_zlib_deflate.c src/main/c/jni_zlib_inflate.c \ diff --git a/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java b/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java index 19dead4ac..e72d92efc 100644 --- a/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java +++ b/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java @@ -63,6 +63,23 @@ public class LibdeflateVelocityCompressor implements VelocityCompressor { } } + @Override + public boolean deflateSingle(ByteBuf source, ByteBuf destination) throws DataFormatException { + ensureNotDisposed(); + + long sourceAddress = source.memoryAddress() + source.readerIndex(); + long destinationAddress = destination.memoryAddress() + destination.writerIndex(); + + int produced = deflate.process(deflateCtx, sourceAddress, source.readableBytes(), + destinationAddress, destination.writableBytes()); + if (produced > 0) { + destination.writerIndex(destination.writerIndex() + produced); + return true; + } + + return false; + } + private void ensureNotDisposed() { Preconditions.checkState(!disposed, "Object already disposed"); } diff --git a/native/src/main/java/com/velocitypowered/natives/compression/VelocityCompressor.java b/native/src/main/java/com/velocitypowered/natives/compression/VelocityCompressor.java index 09cad102c..997b234bc 100644 --- a/native/src/main/java/com/velocitypowered/natives/compression/VelocityCompressor.java +++ b/native/src/main/java/com/velocitypowered/natives/compression/VelocityCompressor.java @@ -14,4 +14,9 @@ public interface VelocityCompressor extends Disposable, Native { throws DataFormatException; void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException; + + default boolean deflateSingle(ByteBuf source, ByteBuf destination) throws DataFormatException { + deflate(source, destination); + return true; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 54c446a88..3c972be0b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -15,6 +15,7 @@ import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; +import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.api.util.Favicon; import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.ProxyVersion; @@ -34,8 +35,11 @@ import com.velocitypowered.proxy.plugin.VelocityEventManager; import com.velocitypowered.proxy.plugin.VelocityPluginManager; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.Chat; -import com.velocitypowered.proxy.protocol.util.FaviconSerializer; +import com.velocitypowered.proxy.protocol.util.ping.FaviconSerializer; import com.velocitypowered.proxy.protocol.util.GameProfileSerializer; +import com.velocitypowered.proxy.protocol.util.ping.PlayersSerializer; +import com.velocitypowered.proxy.protocol.util.ping.ServerPingSerializer; +import com.velocitypowered.proxy.protocol.util.ping.VersionSerializer; import com.velocitypowered.proxy.scheduler.VelocityScheduler; import com.velocitypowered.proxy.server.ServerMap; import com.velocitypowered.proxy.util.AddressUtil; @@ -77,8 +81,6 @@ import java.util.stream.Collectors; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TextComponent; -import net.kyori.adventure.text.TranslatableComponent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.asynchttpclient.AsyncHttpClient; @@ -91,20 +93,26 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { private static final Logger logger = LogManager.getLogger(VelocityServer.class); public static final Gson GENERAL_GSON = new GsonBuilder() - .registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE) + .registerTypeAdapter(Favicon.class, FaviconSerializer.INSTANCE) .registerTypeHierarchyAdapter(GameProfile.class, GameProfileSerializer.INSTANCE) .create(); private static final Gson PRE_1_16_PING_SERIALIZER = ProtocolUtils .getJsonChatSerializer(ProtocolVersion.MINECRAFT_1_15_2) .serializer() .newBuilder() - .registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE) + .registerTypeAdapter(Favicon.class, FaviconSerializer.INSTANCE) + .registerTypeAdapter(ServerPing.class, ServerPingSerializer.INSTANCE) + .registerTypeAdapter(ServerPing.Version.class, VersionSerializer.INSTANCE) + .registerTypeAdapter(ServerPing.Players.class, PlayersSerializer.INSTANCE) .create(); private static final Gson POST_1_16_PING_SERIALIZER = ProtocolUtils .getJsonChatSerializer(ProtocolVersion.MINECRAFT_1_16) .serializer() .newBuilder() - .registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE) + .registerTypeAdapter(Favicon.class, FaviconSerializer.INSTANCE) + .registerTypeAdapter(ServerPing.class, ServerPingSerializer.INSTANCE) + .registerTypeAdapter(ServerPing.Version.class, VersionSerializer.INSTANCE) + .registerTypeAdapter(ServerPing.Players.class, PlayersSerializer.INSTANCE) .create(); private final ConnectionManager cm; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java index a3e36f4cc..b2640a7a8 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java @@ -21,14 +21,20 @@ public class MinecraftCompressEncoder extends MessageToByteEncoder { protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception { int uncompressed = msg.readableBytes(); if (uncompressed <= threshold) { - // Under the threshold, there is nothing to do. + // Under the threshold, only indicate the message was too small ProtocolUtils.writeVarInt(out, 0); out.writeBytes(msg); } else { ProtocolUtils.writeVarInt(out, uncompressed); ByteBuf compatibleIn = MoreByteBufUtils.ensureCompatible(ctx.alloc(), compressor, msg); try { - compressor.deflate(compatibleIn, out); + if (!compressor.deflateSingle(compatibleIn, out)) { + // The packet would have been too big. Clear the output buffer and use an uncompressed + // packet instead. + out.clear(); + ProtocolUtils.writeVarInt(out, 0); + out.writeBytes(msg); + } } finally { compatibleIn.release(); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java deleted file mode 100644 index a67862323..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.velocitypowered.proxy.protocol.util; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.velocitypowered.api.util.Favicon; -import java.lang.reflect.Type; - -public final class FaviconSerializer implements JsonSerializer, JsonDeserializer { - - public static final FaviconSerializer INSTANCE = new FaviconSerializer(); - - private FaviconSerializer() { - - } - - @Override - public Favicon deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { - return new Favicon(json.getAsString()); - } - - @Override - public JsonElement serialize(Favicon src, Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive(src.getBase64Url()); - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/FaviconSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/FaviconSerializer.java new file mode 100644 index 000000000..30651dea2 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/FaviconSerializer.java @@ -0,0 +1,26 @@ +package com.velocitypowered.proxy.protocol.util.ping; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.velocitypowered.api.util.Favicon; +import java.io.IOException; + +public final class FaviconSerializer extends TypeAdapter { + + public static final FaviconSerializer INSTANCE = new FaviconSerializer(); + + private FaviconSerializer() { + + } + + @Override + public void write(JsonWriter writer, Favicon favicon) throws IOException { + writer.value(favicon.getBase64Url()); + } + + @Override + public Favicon read(JsonReader reader) throws IOException { + return new Favicon(reader.nextString()); + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/PlayersSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/PlayersSerializer.java new file mode 100644 index 000000000..39ad5dfd0 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/PlayersSerializer.java @@ -0,0 +1,45 @@ +package com.velocitypowered.proxy.protocol.util.ping; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.reflect.TypeToken; +import com.velocitypowered.api.proxy.server.ServerPing.Players; +import com.velocitypowered.api.proxy.server.ServerPing.SamplePlayer; +import java.lang.reflect.Type; +import java.util.List; + +public class PlayersSerializer implements JsonSerializer, JsonDeserializer { + + public static final PlayersSerializer INSTANCE = new PlayersSerializer(); + + private PlayersSerializer() { + + } + + @Override + public Players deserialize(JsonElement elem, Type type, + JsonDeserializationContext ctx) throws JsonParseException { + JsonObject object = new JsonObject(); + int online = object.get("online").getAsInt(); + int max = object.get("max").getAsInt(); + List sample = ctx.deserialize(object.get("sample"), + new TypeToken>() {}.getType()); + return new Players(online, max, sample); + } + + @Override + public JsonElement serialize(Players players, Type type, JsonSerializationContext ctx) { + JsonObject object = new JsonObject(); + + object.addProperty("online", players.getOnline()); + object.addProperty("max", players.getMax()); + object.add("sample", ctx.serialize(players.getSample())); + + return object; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/ServerPingSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/ServerPingSerializer.java new file mode 100644 index 000000000..d2547a68d --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/ServerPingSerializer.java @@ -0,0 +1,52 @@ +package com.velocitypowered.proxy.protocol.util.ping; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.velocitypowered.api.proxy.server.ServerPing; +import com.velocitypowered.api.proxy.server.ServerPing.Players; +import com.velocitypowered.api.proxy.server.ServerPing.Version; +import com.velocitypowered.api.util.Favicon; +import com.velocitypowered.api.util.ModInfo; +import java.lang.reflect.Type; +import net.kyori.adventure.text.Component; + +public class ServerPingSerializer implements JsonSerializer, + JsonDeserializer { + + public static final ServerPingSerializer INSTANCE = new ServerPingSerializer(); + + private ServerPingSerializer() { + + } + + @Override + public ServerPing deserialize(JsonElement elem, Type type, + JsonDeserializationContext ctx) throws JsonParseException { + JsonObject object = elem.getAsJsonObject(); + + Component description = ctx.deserialize(object.get("description"), Component.class); + Version version = ctx.deserialize(object.get("version"), Version.class); + Players players = ctx.deserialize(object.get("players"), Players.class); + Favicon favicon = ctx.deserialize(object.get("favicon"), Favicon.class); + ModInfo modInfo = ctx.deserialize(object.get("modInfo"), ModInfo.class); + + return new ServerPing(version, players, description, favicon, modInfo); + } + + @Override + public JsonElement serialize(ServerPing ping, Type type, + JsonSerializationContext ctx) { + JsonObject object = new JsonObject(); + object.add("description", ctx.serialize(ping.getDescriptionComponent())); + object.add("version", ctx.serialize(ping.getVersion())); + object.add("players", ctx.serialize(ping.getPlayers().orElse(null))); + object.addProperty("favicon", ping.getFavicon().map(Favicon::getBase64Url).orElse(null)); + object.add("modInfo", ctx.serialize(ping.getModinfo().orElse(null))); + return object; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/VersionSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/VersionSerializer.java new file mode 100644 index 000000000..198c67578 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/VersionSerializer.java @@ -0,0 +1,47 @@ +package com.velocitypowered.proxy.protocol.util.ping; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.velocitypowered.api.proxy.server.ServerPing.Version; +import java.io.IOException; + +public class VersionSerializer extends TypeAdapter { + + public static final VersionSerializer INSTANCE = new VersionSerializer(); + + private VersionSerializer() { + + } + + @Override + public void write(JsonWriter jsonWriter, Version version) throws IOException { + jsonWriter.beginObject(); + jsonWriter.name("protocol"); + jsonWriter.value(version.getProtocol()); + jsonWriter.name("name"); + jsonWriter.value(version.getName()); + jsonWriter.endObject(); + } + + @Override + public Version read(JsonReader jsonReader) throws IOException { + jsonReader.beginObject(); + + String name = ""; + int protocol = -1; + for (int i = 0; i < 2; i++) { + String elem = jsonReader.nextName(); + if (elem.equals("name")) { + name = jsonReader.nextString(); + } else if (elem.equals("protocol")) { + protocol = jsonReader.nextInt(); + } else { + throw new IllegalStateException("Invalid version specification."); + } + } + + jsonReader.endObject(); + return new Version(protocol, name); + } +} From 706fea8a3cf45d5e1836ee66df22c91e0b9a7252 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 4 Oct 2020 15:31:59 -0400 Subject: [PATCH 12/39] Fix checkstyle error --- .../src/main/java/com/velocitypowered/proxy/VelocityServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 3c972be0b..6286640f6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -35,8 +35,8 @@ import com.velocitypowered.proxy.plugin.VelocityEventManager; import com.velocitypowered.proxy.plugin.VelocityPluginManager; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.Chat; -import com.velocitypowered.proxy.protocol.util.ping.FaviconSerializer; import com.velocitypowered.proxy.protocol.util.GameProfileSerializer; +import com.velocitypowered.proxy.protocol.util.ping.FaviconSerializer; import com.velocitypowered.proxy.protocol.util.ping.PlayersSerializer; import com.velocitypowered.proxy.protocol.util.ping.ServerPingSerializer; import com.velocitypowered.proxy.protocol.util.ping.VersionSerializer; From 5072e1085a764cd5f9ce6a6889497a04777d4ad9 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 6 Oct 2020 11:05:36 -0400 Subject: [PATCH 13/39] Revert server list ping changes They are broken with ping passthrough. --- .../LibdeflateVelocityCompressor.java | 17 ------ .../compression/VelocityCompressor.java | 5 -- .../velocitypowered/proxy/VelocityServer.java | 20 +++---- .../netty/MinecraftCompressEncoder.java | 10 +--- .../protocol/util/FaviconSerializer.java | 29 +++++++++++ .../protocol/util/ping/FaviconSerializer.java | 26 ---------- .../protocol/util/ping/PlayersSerializer.java | 45 ---------------- .../util/ping/ServerPingSerializer.java | 52 ------------------- .../protocol/util/ping/VersionSerializer.java | 47 ----------------- 9 files changed, 37 insertions(+), 214 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/FaviconSerializer.java delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/PlayersSerializer.java delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/ServerPingSerializer.java delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/VersionSerializer.java diff --git a/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java b/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java index e72d92efc..19dead4ac 100644 --- a/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java +++ b/native/src/main/java/com/velocitypowered/natives/compression/LibdeflateVelocityCompressor.java @@ -63,23 +63,6 @@ public class LibdeflateVelocityCompressor implements VelocityCompressor { } } - @Override - public boolean deflateSingle(ByteBuf source, ByteBuf destination) throws DataFormatException { - ensureNotDisposed(); - - long sourceAddress = source.memoryAddress() + source.readerIndex(); - long destinationAddress = destination.memoryAddress() + destination.writerIndex(); - - int produced = deflate.process(deflateCtx, sourceAddress, source.readableBytes(), - destinationAddress, destination.writableBytes()); - if (produced > 0) { - destination.writerIndex(destination.writerIndex() + produced); - return true; - } - - return false; - } - private void ensureNotDisposed() { Preconditions.checkState(!disposed, "Object already disposed"); } diff --git a/native/src/main/java/com/velocitypowered/natives/compression/VelocityCompressor.java b/native/src/main/java/com/velocitypowered/natives/compression/VelocityCompressor.java index 997b234bc..09cad102c 100644 --- a/native/src/main/java/com/velocitypowered/natives/compression/VelocityCompressor.java +++ b/native/src/main/java/com/velocitypowered/natives/compression/VelocityCompressor.java @@ -14,9 +14,4 @@ public interface VelocityCompressor extends Disposable, Native { throws DataFormatException; void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException; - - default boolean deflateSingle(ByteBuf source, ByteBuf destination) throws DataFormatException { - deflate(source, destination); - return true; - } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 6286640f6..54c446a88 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -15,7 +15,6 @@ import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; -import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.api.util.Favicon; import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.ProxyVersion; @@ -35,11 +34,8 @@ import com.velocitypowered.proxy.plugin.VelocityEventManager; import com.velocitypowered.proxy.plugin.VelocityPluginManager; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.Chat; +import com.velocitypowered.proxy.protocol.util.FaviconSerializer; import com.velocitypowered.proxy.protocol.util.GameProfileSerializer; -import com.velocitypowered.proxy.protocol.util.ping.FaviconSerializer; -import com.velocitypowered.proxy.protocol.util.ping.PlayersSerializer; -import com.velocitypowered.proxy.protocol.util.ping.ServerPingSerializer; -import com.velocitypowered.proxy.protocol.util.ping.VersionSerializer; import com.velocitypowered.proxy.scheduler.VelocityScheduler; import com.velocitypowered.proxy.server.ServerMap; import com.velocitypowered.proxy.util.AddressUtil; @@ -81,6 +77,8 @@ import java.util.stream.Collectors; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.TranslatableComponent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.asynchttpclient.AsyncHttpClient; @@ -93,26 +91,20 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { private static final Logger logger = LogManager.getLogger(VelocityServer.class); public static final Gson GENERAL_GSON = new GsonBuilder() - .registerTypeAdapter(Favicon.class, FaviconSerializer.INSTANCE) + .registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE) .registerTypeHierarchyAdapter(GameProfile.class, GameProfileSerializer.INSTANCE) .create(); private static final Gson PRE_1_16_PING_SERIALIZER = ProtocolUtils .getJsonChatSerializer(ProtocolVersion.MINECRAFT_1_15_2) .serializer() .newBuilder() - .registerTypeAdapter(Favicon.class, FaviconSerializer.INSTANCE) - .registerTypeAdapter(ServerPing.class, ServerPingSerializer.INSTANCE) - .registerTypeAdapter(ServerPing.Version.class, VersionSerializer.INSTANCE) - .registerTypeAdapter(ServerPing.Players.class, PlayersSerializer.INSTANCE) + .registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE) .create(); private static final Gson POST_1_16_PING_SERIALIZER = ProtocolUtils .getJsonChatSerializer(ProtocolVersion.MINECRAFT_1_16) .serializer() .newBuilder() - .registerTypeAdapter(Favicon.class, FaviconSerializer.INSTANCE) - .registerTypeAdapter(ServerPing.class, ServerPingSerializer.INSTANCE) - .registerTypeAdapter(ServerPing.Version.class, VersionSerializer.INSTANCE) - .registerTypeAdapter(ServerPing.Players.class, PlayersSerializer.INSTANCE) + .registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE) .create(); private final ConnectionManager cm; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java index b2640a7a8..a3e36f4cc 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java @@ -21,20 +21,14 @@ public class MinecraftCompressEncoder extends MessageToByteEncoder { protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception { int uncompressed = msg.readableBytes(); if (uncompressed <= threshold) { - // Under the threshold, only indicate the message was too small + // Under the threshold, there is nothing to do. ProtocolUtils.writeVarInt(out, 0); out.writeBytes(msg); } else { ProtocolUtils.writeVarInt(out, uncompressed); ByteBuf compatibleIn = MoreByteBufUtils.ensureCompatible(ctx.alloc(), compressor, msg); try { - if (!compressor.deflateSingle(compatibleIn, out)) { - // The packet would have been too big. Clear the output buffer and use an uncompressed - // packet instead. - out.clear(); - ProtocolUtils.writeVarInt(out, 0); - out.writeBytes(msg); - } + compressor.deflate(compatibleIn, out); } finally { compatibleIn.release(); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java new file mode 100644 index 000000000..a67862323 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java @@ -0,0 +1,29 @@ +package com.velocitypowered.proxy.protocol.util; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.velocitypowered.api.util.Favicon; +import java.lang.reflect.Type; + +public final class FaviconSerializer implements JsonSerializer, JsonDeserializer { + + public static final FaviconSerializer INSTANCE = new FaviconSerializer(); + + private FaviconSerializer() { + + } + + @Override + public Favicon deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { + return new Favicon(json.getAsString()); + } + + @Override + public JsonElement serialize(Favicon src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.getBase64Url()); + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/FaviconSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/FaviconSerializer.java deleted file mode 100644 index 30651dea2..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/FaviconSerializer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.velocitypowered.proxy.protocol.util.ping; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import com.velocitypowered.api.util.Favicon; -import java.io.IOException; - -public final class FaviconSerializer extends TypeAdapter { - - public static final FaviconSerializer INSTANCE = new FaviconSerializer(); - - private FaviconSerializer() { - - } - - @Override - public void write(JsonWriter writer, Favicon favicon) throws IOException { - writer.value(favicon.getBase64Url()); - } - - @Override - public Favicon read(JsonReader reader) throws IOException { - return new Favicon(reader.nextString()); - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/PlayersSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/PlayersSerializer.java deleted file mode 100644 index 39ad5dfd0..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/PlayersSerializer.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.velocitypowered.proxy.protocol.util.ping; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.google.gson.reflect.TypeToken; -import com.velocitypowered.api.proxy.server.ServerPing.Players; -import com.velocitypowered.api.proxy.server.ServerPing.SamplePlayer; -import java.lang.reflect.Type; -import java.util.List; - -public class PlayersSerializer implements JsonSerializer, JsonDeserializer { - - public static final PlayersSerializer INSTANCE = new PlayersSerializer(); - - private PlayersSerializer() { - - } - - @Override - public Players deserialize(JsonElement elem, Type type, - JsonDeserializationContext ctx) throws JsonParseException { - JsonObject object = new JsonObject(); - int online = object.get("online").getAsInt(); - int max = object.get("max").getAsInt(); - List sample = ctx.deserialize(object.get("sample"), - new TypeToken>() {}.getType()); - return new Players(online, max, sample); - } - - @Override - public JsonElement serialize(Players players, Type type, JsonSerializationContext ctx) { - JsonObject object = new JsonObject(); - - object.addProperty("online", players.getOnline()); - object.addProperty("max", players.getMax()); - object.add("sample", ctx.serialize(players.getSample())); - - return object; - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/ServerPingSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/ServerPingSerializer.java deleted file mode 100644 index d2547a68d..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/ServerPingSerializer.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.velocitypowered.proxy.protocol.util.ping; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.velocitypowered.api.proxy.server.ServerPing; -import com.velocitypowered.api.proxy.server.ServerPing.Players; -import com.velocitypowered.api.proxy.server.ServerPing.Version; -import com.velocitypowered.api.util.Favicon; -import com.velocitypowered.api.util.ModInfo; -import java.lang.reflect.Type; -import net.kyori.adventure.text.Component; - -public class ServerPingSerializer implements JsonSerializer, - JsonDeserializer { - - public static final ServerPingSerializer INSTANCE = new ServerPingSerializer(); - - private ServerPingSerializer() { - - } - - @Override - public ServerPing deserialize(JsonElement elem, Type type, - JsonDeserializationContext ctx) throws JsonParseException { - JsonObject object = elem.getAsJsonObject(); - - Component description = ctx.deserialize(object.get("description"), Component.class); - Version version = ctx.deserialize(object.get("version"), Version.class); - Players players = ctx.deserialize(object.get("players"), Players.class); - Favicon favicon = ctx.deserialize(object.get("favicon"), Favicon.class); - ModInfo modInfo = ctx.deserialize(object.get("modInfo"), ModInfo.class); - - return new ServerPing(version, players, description, favicon, modInfo); - } - - @Override - public JsonElement serialize(ServerPing ping, Type type, - JsonSerializationContext ctx) { - JsonObject object = new JsonObject(); - object.add("description", ctx.serialize(ping.getDescriptionComponent())); - object.add("version", ctx.serialize(ping.getVersion())); - object.add("players", ctx.serialize(ping.getPlayers().orElse(null))); - object.addProperty("favicon", ping.getFavicon().map(Favicon::getBase64Url).orElse(null)); - object.add("modInfo", ctx.serialize(ping.getModinfo().orElse(null))); - return object; - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/VersionSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/VersionSerializer.java deleted file mode 100644 index 198c67578..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ping/VersionSerializer.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.velocitypowered.proxy.protocol.util.ping; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import com.velocitypowered.api.proxy.server.ServerPing.Version; -import java.io.IOException; - -public class VersionSerializer extends TypeAdapter { - - public static final VersionSerializer INSTANCE = new VersionSerializer(); - - private VersionSerializer() { - - } - - @Override - public void write(JsonWriter jsonWriter, Version version) throws IOException { - jsonWriter.beginObject(); - jsonWriter.name("protocol"); - jsonWriter.value(version.getProtocol()); - jsonWriter.name("name"); - jsonWriter.value(version.getName()); - jsonWriter.endObject(); - } - - @Override - public Version read(JsonReader jsonReader) throws IOException { - jsonReader.beginObject(); - - String name = ""; - int protocol = -1; - for (int i = 0; i < 2; i++) { - String elem = jsonReader.nextName(); - if (elem.equals("name")) { - name = jsonReader.nextString(); - } else if (elem.equals("protocol")) { - protocol = jsonReader.nextInt(); - } else { - throw new IllegalStateException("Invalid version specification."); - } - } - - jsonReader.endObject(); - return new Version(protocol, name); - } -} From f58b78e896d7c16210d6a307a8f532a7f04d64f4 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 8 Oct 2020 14:43:34 -0400 Subject: [PATCH 14/39] Fix typo that emitted full responses for basic ones and vice versa --- .../velocitypowered/proxy/protocol/netty/GS4QueryHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java index 4ea37de84..888f6ab29 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java @@ -128,7 +128,7 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler // Build initial query response QueryResponse response = createInitialResponse(); - boolean isBasic = queryMessage.isReadable(); + boolean isBasic = !queryMessage.isReadable(); // Call event and write response server.getEventManager() From 7fea1c4cb212df197a7f08ba509768d39369535a Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 10 Oct 2020 10:58:32 -0400 Subject: [PATCH 15/39] Fix modern forwarding if a user connects over IPv6(?!?) You don't see this every day... --- .../connection/backend/LoginSessionHandler.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java index dc839640c..80e6bd7ce 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java @@ -20,6 +20,7 @@ import com.velocitypowered.proxy.protocol.packet.SetCompression; import com.velocitypowered.proxy.util.except.QuietRuntimeException; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import java.net.InetSocketAddress; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.concurrent.CompletableFuture; @@ -58,7 +59,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN && packet .getChannel().equals(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL)) { ByteBuf forwardingData = createForwardingData(configuration.getForwardingSecret(), - serverConn.getPlayer().getRemoteAddress().getHostString(), + cleanRemoteAddress(serverConn.getPlayer().getRemoteAddress()), serverConn.getPlayer().getGameProfile()); LoginPluginResponse response = new LoginPluginResponse(packet.getId(), true, forwardingData); mc.write(response); @@ -127,6 +128,16 @@ public class LoginSessionHandler implements MinecraftSessionHandler { } } + private static String cleanRemoteAddress(InetSocketAddress address) { + String addressString = address.getAddress().getHostAddress(); + int ipv6ScopeIdx = addressString.indexOf('%'); + if (ipv6ScopeIdx == -1) { + return addressString; + } else { + return addressString.substring(0, ipv6ScopeIdx); + } + } + private static ByteBuf createForwardingData(byte[] hmacSecret, String address, GameProfile profile) { ByteBuf forwarded = Unpooled.buffer(2048); From 59d8bd4c78a2a6baaeaecb690d2d22dce41e49d4 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 11 Oct 2020 14:36:56 -0400 Subject: [PATCH 16/39] Plugins need to be stored in a LinkedHashMap too. --- .../com/velocitypowered/proxy/plugin/VelocityPluginManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java index 1a2301c38..232e8c254 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java @@ -41,7 +41,7 @@ public class VelocityPluginManager implements PluginManager { private static final Logger logger = LogManager.getLogger(VelocityPluginManager.class); - private final Map plugins = new HashMap<>(); + private final Map plugins = new LinkedHashMap<>(); private final Map pluginInstances = new IdentityHashMap<>(); private final VelocityServer server; From 3b1009caba5ffc02e9b2d1ddd7ff70c204f7ad3a Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 11 Oct 2020 20:15:29 -0400 Subject: [PATCH 17/39] Prepare for 1.16.4 --- .../java/com/velocitypowered/api/network/ProtocolVersion.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 27c40d11f..5a6b0f60b 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -40,7 +40,8 @@ public enum ProtocolVersion { MINECRAFT_1_16(735, "1.16"), MINECRAFT_1_16_1(736, "1.16.1"), MINECRAFT_1_16_2(751, "1.16.2"), - MINECRAFT_1_16_3(753, "1.16.3"); + MINECRAFT_1_16_3(753, "1.16.3"), + MINECRAFT_1_16_4(754, "1.16.4"); private final int protocol; private final String name; From 60e917b4a104d02f9175013e3ce1a42c5daa4ae9 Mon Sep 17 00:00:00 2001 From: Riley Park Date: Mon, 12 Oct 2020 16:47:49 -0700 Subject: [PATCH 18/39] Player has an identity --- .../api/command/CommandSource.java | 9 ++++--- .../com/velocitypowered/api/proxy/Player.java | 5 ++-- .../api/proxy/ProxyAudience.java | 25 ------------------- .../proxy/command/VelocityCommandManager.java | 3 ++- .../proxy/command/builtin/GlistCommand.java | 10 +++++--- .../proxy/command/builtin/ServerCommand.java | 13 +++++----- .../command/builtin/VelocityCommand.java | 25 +++++++++++-------- .../backend/BungeeCordMessageResponder.java | 5 ++-- .../client/ClientPlaySessionHandler.java | 3 ++- .../connection/client/ConnectedPlayer.java | 24 ++++++++++++++---- .../proxy/console/VelocityConsole.java | 4 ++- .../proxy/protocol/packet/Chat.java | 7 +++--- 12 files changed, 69 insertions(+), 64 deletions(-) delete mode 100644 api/src/main/java/com/velocitypowered/api/proxy/ProxyAudience.java 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 d2862d404..346b21c27 100644 --- a/api/src/main/java/com/velocitypowered/api/command/CommandSource.java +++ b/api/src/main/java/com/velocitypowered/api/command/CommandSource.java @@ -1,7 +1,9 @@ package com.velocitypowered.api.command; import com.velocitypowered.api.permission.PermissionSubject; -import com.velocitypowered.api.proxy.ProxyAudience; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.audience.MessageType; +import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.legacytext3.LegacyText3ComponentSerializer; import org.checkerframework.checker.nullness.qual.NonNull; @@ -9,7 +11,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; /** * Represents something that can be used to run a {@link Command}. */ -public interface CommandSource extends PermissionSubject, ProxyAudience { +public interface CommandSource extends Audience, PermissionSubject { /** * Sends the specified {@code component} to the invoker. @@ -21,7 +23,8 @@ public interface CommandSource extends PermissionSubject, ProxyAudience { void sendMessage(net.kyori.text.Component component); @Override - default void sendMessage(@NonNull Component message) { + default void sendMessage(@NonNull Identity identity, @NonNull Component message, + @NonNull MessageType type) { this.sendMessage(LegacyText3ComponentSerializer.get().serialize(message)); } } 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 dce8a5c4b..3761843e2 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -14,13 +14,14 @@ import com.velocitypowered.api.util.title.Title; import java.util.List; import java.util.Optional; import java.util.UUID; +import net.kyori.adventure.identity.Identified; import net.kyori.adventure.text.Component; /** * Represents a player who is connected to the proxy. */ -public interface Player extends CommandSource, InboundConnection, ChannelMessageSource, - ChannelMessageSink { +public interface Player extends CommandSource, Identified, InboundConnection, + ChannelMessageSource, ChannelMessageSink { /** * Returns the player's current username. diff --git a/api/src/main/java/com/velocitypowered/api/proxy/ProxyAudience.java b/api/src/main/java/com/velocitypowered/api/proxy/ProxyAudience.java deleted file mode 100644 index 0b06b8f9d..000000000 --- a/api/src/main/java/com/velocitypowered/api/proxy/ProxyAudience.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.velocitypowered.api.proxy; - -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.audience.MessageType; -import net.kyori.adventure.text.Component; -import org.checkerframework.checker.nullness.qual.NonNull; - -/** - * Indicates an {@link Audience} that is on the proxy. This interface contains no-op default methods - * that are used to bridge compatibility issues with the new adventure API. This interface will go - * away in Velocity 2.0.0. - * - * @deprecated Only used to handle compatibility problems, will go away in Velocity 2.0.0 - */ -@Deprecated -public interface ProxyAudience extends Audience { - - @Override - void sendMessage(@NonNull Component message); - - @Override - default void sendMessage(@NonNull Component message, @NonNull MessageType type) { - sendMessage(message); - } -} 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 491a14aff..92232a380 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -24,6 +24,7 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.concurrent.CompletableFuture; +import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; @@ -162,7 +163,7 @@ public class VelocityCommandManager implements CommandManager { boolean isSyntaxError = !e.getType().equals( CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()); if (isSyntaxError) { - source.sendMessage(Component.text(e.getMessage(), NamedTextColor.RED)); + source.sendMessage(Identity.nil(), Component.text(e.getMessage(), NamedTextColor.RED)); // This is, of course, a lie, but the API will need to change... return true; } else { 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 9337d4ea7..f3b930ea0 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 @@ -17,6 +17,7 @@ import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.server.RegisteredServer; import java.util.List; import java.util.Optional; +import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; @@ -59,7 +60,7 @@ public class GlistCommand { private int totalCount(final CommandContext context) { final CommandSource source = context.getSource(); sendTotalProxyCount(source); - source.sendMessage( + source.sendMessage(Identity.nil(), Component.text().content("To view all players on servers, use ") .color(NamedTextColor.YELLOW) .append(Component.text("/glist all", NamedTextColor.DARK_AQUA)) @@ -79,7 +80,7 @@ public class GlistCommand { } else { Optional registeredServer = server.getServer(serverName); if (!registeredServer.isPresent()) { - source.sendMessage( + source.sendMessage(Identity.nil(), Component.text("Server " + serverName + " doesn't exist.", NamedTextColor.RED)); return -1; } @@ -89,7 +90,8 @@ public class GlistCommand { } private void sendTotalProxyCount(CommandSource target) { - target.sendMessage(Component.text().content("There are ").color(NamedTextColor.YELLOW) + target.sendMessage(Identity.nil(), Component.text() + .content("There are ").color(NamedTextColor.YELLOW) .append(Component.text(server.getAllPlayers().size(), NamedTextColor.GREEN)) .append(Component.text(" player(s) online.", NamedTextColor.YELLOW)) .build()); @@ -117,6 +119,6 @@ public class GlistCommand { } } - target.sendMessage(builder.build()); + target.sendMessage(Identity.nil(), builder.build()); } } 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 4c606902b..145f914b4 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 @@ -15,6 +15,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; +import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.event.ClickEvent; @@ -35,7 +36,7 @@ public class ServerCommand implements SimpleCommand { final String[] args = invocation.arguments(); if (!(source instanceof Player)) { - source.sendMessage(Component.text("Only players may run this command.", + source.sendMessage(Identity.nil(), Component.text("Only players may run this command.", NamedTextColor.RED)); return; } @@ -46,7 +47,7 @@ public class ServerCommand implements SimpleCommand { String serverName = args[0]; Optional toConnect = server.getServer(serverName); if (!toConnect.isPresent()) { - player.sendMessage( + player.sendMessage(Identity.nil(), Component.text("Server " + serverName + " doesn't exist.", NamedTextColor.RED)); return; } @@ -60,12 +61,12 @@ public class ServerCommand implements SimpleCommand { private void outputServerInformation(Player executor) { String currentServer = executor.getCurrentServer().map(ServerConnection::getServerInfo) .map(ServerInfo::getName).orElse(""); - executor.sendMessage(Component.text("You are currently connected to " + currentServer + ".", - NamedTextColor.YELLOW)); + executor.sendMessage(Identity.nil(), Component.text( + "You are currently connected to " + currentServer + ".", NamedTextColor.YELLOW)); List servers = BuiltinCommandUtil.sortedServerList(server); if (servers.size() > MAX_SERVERS_TO_LIST) { - executor.sendMessage(Component.text( + executor.sendMessage(Identity.nil(), Component.text( "Too many servers to list. Tab-complete to show all servers.", NamedTextColor.RED)); return; } @@ -81,7 +82,7 @@ public class ServerCommand implements SimpleCommand { } } - executor.sendMessage(serverListBuilder.build()); + executor.sendMessage(Identity.nil(), serverListBuilder.build()); } private TextComponent formatServerComponent(String currentPlayerServer, RegisteredServer server) { 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 47cc3fb93..db5714b2b 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 @@ -16,6 +16,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; +import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.event.ClickEvent; @@ -60,7 +61,7 @@ public class VelocityCommand implements SimpleCommand { .map(Map.Entry::getKey) .collect(Collectors.joining("|")); String commandText = "/velocity <" + availableCommands + ">"; - source.sendMessage(Component.text(commandText, NamedTextColor.RED)); + source.sendMessage(Identity.nil(), Component.text(commandText, NamedTextColor.RED)); } @Override @@ -143,15 +144,16 @@ public class VelocityCommand implements SimpleCommand { public void execute(CommandSource source, String @NonNull [] args) { try { if (server.reloadConfiguration()) { - source.sendMessage(Component.text("Configuration reloaded.", NamedTextColor.GREEN)); + source.sendMessage(Identity.nil(), Component.text( + "Configuration reloaded.", NamedTextColor.GREEN)); } else { - source.sendMessage(Component.text( + source.sendMessage(Identity.nil(), Component.text( "Unable to reload your configuration. Check the console for more details.", NamedTextColor.RED)); } } catch (Exception e) { logger.error("Unable to reload configuration", e); - source.sendMessage(Component.text( + source.sendMessage(Identity.nil(), Component.text( "Unable to reload your configuration. Check the console for more details.", NamedTextColor.RED)); } @@ -174,7 +176,7 @@ public class VelocityCommand implements SimpleCommand { @Override public void execute(CommandSource source, String @NonNull [] args) { if (args.length != 0) { - source.sendMessage(Component.text("/velocity version", NamedTextColor.RED)); + source.sendMessage(Identity.nil(), Component.text("/velocity version", NamedTextColor.RED)); return; } @@ -188,8 +190,8 @@ public class VelocityCommand implements SimpleCommand { TextComponent copyright = TextComponent .of("Copyright 2018-2020 " + version.getVendor() + ". " + version.getName() + " is freely licensed under the terms of the MIT License."); - source.sendMessage(velocity); - source.sendMessage(copyright); + source.sendMessage(Identity.nil(), velocity); + source.sendMessage(Identity.nil(), copyright); if (version.getName().equals("Velocity")) { TextComponent velocityWebsite = Component.text() @@ -206,7 +208,7 @@ public class VelocityCommand implements SimpleCommand { "https://github.com/VelocityPowered/Velocity")) .build()) .build(); - source.sendMessage(velocityWebsite); + source.sendMessage(Identity.nil(), velocityWebsite); } } @@ -227,7 +229,7 @@ public class VelocityCommand implements SimpleCommand { @Override public void execute(CommandSource source, String @NonNull [] args) { if (args.length != 0) { - source.sendMessage(Component.text("/velocity plugins", NamedTextColor.RED)); + source.sendMessage(Identity.nil(), Component.text("/velocity plugins", NamedTextColor.RED)); return; } @@ -235,7 +237,8 @@ public class VelocityCommand implements SimpleCommand { int pluginCount = plugins.size(); if (pluginCount == 0) { - source.sendMessage(Component.text("No plugins installed.", NamedTextColor.YELLOW)); + source.sendMessage(Identity.nil(), Component.text( + "No plugins installed.", NamedTextColor.YELLOW)); return; } @@ -249,7 +252,7 @@ public class VelocityCommand implements SimpleCommand { } } - source.sendMessage(output.build()); + source.sendMessage(Identity.nil(), output.build()); } private TextComponent componentForPlugin(PluginDescription description) { 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 9cfc0f371..92c4b18ab 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 @@ -18,6 +18,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import java.util.Optional; import java.util.StringJoiner; +import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.ComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; @@ -155,9 +156,9 @@ class BungeeCordMessageResponder { Component messageComponent = serializer.deserialize(message); if (target.equals("ALL")) { - proxy.sendMessage(messageComponent); + proxy.sendMessage(Identity.nil(), messageComponent); } else { - proxy.getPlayer(target).ifPresent(player -> player.sendMessage(messageComponent)); + proxy.getPlayer(target).ifPresent(player -> player.sendMessage(Identity.nil(), messageComponent)); } } 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 846b4cdbf..5717a0f05 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 @@ -43,6 +43,7 @@ import java.util.Optional; import java.util.Queue; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.apache.logging.log4j.LogManager; @@ -137,7 +138,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { .exceptionally(e -> { logger.info("Exception occurred while running command for {}", player.getUsername(), e); - player.sendMessage( + player.sendMessage(Identity.nil(), Component.text("An error occurred while running this command.", NamedTextColor.RED)); return null; 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 3ad998ef4..92bdc6778 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 @@ -68,8 +68,8 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.ThreadLocalRandom; import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.identity.Identity; 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.serializer.gson.GsonComponentSerializer; @@ -90,6 +90,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { private static final Logger logger = LogManager.getLogger(ConnectedPlayer.class); + private final Identity identity = new IdentityImpl(); /** * The actual Minecraft connection. This is actually a wrapper object around the Netty channel. */ @@ -128,6 +129,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { this.onlineMode = onlineMode; } + @Override + public @NonNull Identity identity() { + return this.identity; + } + @Override public String getUsername() { return profile.getName(); @@ -258,16 +264,17 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } @Override - public void sendMessage(net.kyori.adventure.text.@NonNull Component message) { - connection.write(Chat.createClientbound(message, this.getProtocolVersion())); + public void sendMessage(@NonNull Identity identity, @NonNull Component message) { + connection.write(Chat.createClientbound(identity, message, this.getProtocolVersion())); } @Override - public void sendMessage(@NonNull Component message, @NonNull MessageType type) { + public void sendMessage(@NonNull Identity identity, @NonNull Component message, + @NonNull MessageType type) { Preconditions.checkNotNull(message, "message"); Preconditions.checkNotNull(type, "type"); - Chat packet = Chat.createClientbound(message, this.getProtocolVersion()); + Chat packet = Chat.createClientbound(identity, message, this.getProtocolVersion()); packet.setType(type == MessageType.CHAT ? Chat.CHAT_TYPE : Chat.SYSTEM_TYPE); connection.write(packet); } @@ -871,6 +878,13 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return minecraftOrFmlMessage || knownChannels.contains(message.getChannel()); } + private class IdentityImpl implements Identity { + @Override + public @NonNull UUID uuid() { + return ConnectedPlayer.this.getUniqueId(); + } + } + private class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder { private final RegisteredServer toConnect; 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 931e68c82..4273549ed 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java @@ -8,6 +8,8 @@ import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.ConsoleCommandSource; import com.velocitypowered.proxy.VelocityServer; import java.util.List; +import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; import net.minecrell.terminalconsole.SimpleTerminalConsole; @@ -38,7 +40,7 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons } @Override - public void sendMessage(net.kyori.adventure.text.@NonNull Component message) { + public void sendMessage(@NonNull Identity identity, @NonNull Component message) { logger.info(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection() .serialize(message)); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java index a42d2650b..74f0a627d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java @@ -6,6 +6,7 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; +import net.kyori.adventure.identity.Identity; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.UUID; @@ -109,9 +110,9 @@ public class Chat implements MinecraftPacket { .serialize(component), type, sender); } - public static Chat createClientbound(net.kyori.adventure.text.Component component, - ProtocolVersion version) { - return createClientbound(component, CHAT_TYPE, EMPTY_SENDER, version); + public static Chat createClientbound(Identity identity, + net.kyori.adventure.text.Component component, ProtocolVersion version) { + return createClientbound(component, CHAT_TYPE, identity.uuid(), version); } public static Chat createClientbound(net.kyori.adventure.text.Component component, byte type, From cffc6d0a81af206d67ec0bd443c893f475c31e2d Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 14 Oct 2020 13:58:28 -0400 Subject: [PATCH 19/39] Fix Checkstyle error --- api/build.gradle | 4 ++-- .../proxy/connection/backend/BungeeCordMessageResponder.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/api/build.gradle b/api/build.gradle index 25142d71a..a2c07338a 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -79,8 +79,8 @@ javadoc { 'http://www.slf4j.org/apidocs/', 'https://google.github.io/guava/releases/25.1-jre/api/docs/', 'https://google.github.io/guice/api-docs/4.2/javadoc/', - 'https://jd.kyori.net/text-api/3.0.0/', - 'https://docs.oracle.com/javase/8/docs/api/' + 'https://docs.oracle.com/javase/8/docs/api/', + 'https://jd.adventure.kyori.net/api/4.0.0/' ) // Disable the crazy super-strict doclint tool in Java 8 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 92c4b18ab..e8896cf63 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 @@ -158,7 +158,8 @@ class BungeeCordMessageResponder { if (target.equals("ALL")) { proxy.sendMessage(Identity.nil(), messageComponent); } else { - proxy.getPlayer(target).ifPresent(player -> player.sendMessage(Identity.nil(), messageComponent)); + proxy.getPlayer(target).ifPresent(player -> player.sendMessage(Identity.nil(), + messageComponent)); } } From 6e00dbe2b7cff470c8d728f9d64af792afbbc115 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 14 Oct 2020 16:08:20 -0400 Subject: [PATCH 20/39] Fix compile errors --- api/build.gradle | 2 +- build.gradle | 3 ++- .../proxy/command/builtin/GlistCommand.java | 4 ++-- .../proxy/command/builtin/VelocityCommand.java | 4 ++-- .../proxy/connection/client/ConnectedPlayer.java | 8 ++++---- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/api/build.gradle b/api/build.gradle index a2c07338a..4a7fd9130 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -31,7 +31,7 @@ dependencies { api "net.kyori:adventure-text-serializer-gson:${adventureVersion}" api "net.kyori:adventure-text-serializer-legacy:${adventureVersion}" api "net.kyori:adventure-text-serializer-plain:${adventureVersion}" - api "net.kyori:adventure-text-serializer-legacy-text3:${adventureVersion}" + api "net.kyori:adventure-text-serializer-legacy-text3:${adventurePlatformVersion}" api "org.slf4j:slf4j-api:${slf4jVersion}" api 'com.google.inject:guice:4.2.3' diff --git a/build.gradle b/build.gradle index 1f41da8da..ac901337f 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,8 @@ allprojects { ext { // dependency versions textVersion = '3.0.4' - adventureVersion = '4.0.0-SNAPSHOT' + adventureVersion = '4.0.0' + adventurePlatformVersion = '4.0.0-SNAPSHOT' junitVersion = '5.7.0' slf4jVersion = '1.7.30' log4jVersion = '2.13.3' 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 f3b930ea0..ca1677a90 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 @@ -104,7 +104,7 @@ public class GlistCommand { } TextComponent.Builder builder = Component.text() - .append(TextComponent.of("[" + server.getServerInfo().getName() + "] ", + .append(Component.text("[" + server.getServerInfo().getName() + "] ", NamedTextColor.DARK_AQUA)) .append(Component.text("(" + onServer.size() + ")", NamedTextColor.GRAY)) .append(Component.text(": ")) @@ -112,7 +112,7 @@ public class GlistCommand { for (int i = 0; i < onServer.size(); i++) { Player player = onServer.get(i); - builder.append(player.getUsername()); + builder.append(Component.text(player.getUsername())); if (i + 1 < onServer.size()) { builder.append(Component.text(", ")); 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 db5714b2b..02c085241 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 @@ -187,8 +187,8 @@ public class VelocityCommand implements SimpleCommand { .color(NamedTextColor.DARK_AQUA) .append(Component.text(version.getVersion()).decoration(TextDecoration.BOLD, false)) .build(); - TextComponent copyright = TextComponent - .of("Copyright 2018-2020 " + version.getVendor() + ". " + version.getName() + TextComponent copyright = Component + .text("Copyright 2018-2020 " + version.getVendor() + ". " + version.getName() + " is freely licensed under the terms of the MIT License."); source.sendMessage(Identity.nil(), velocity); source.sendMessage(Identity.nil(), copyright); 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 92bdc6778..9672f3918 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 @@ -630,7 +630,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { getProtocolVersion()), ((Impl) status).isSafe()); break; case SUCCESS: - sendMessage(server.getConfiguration().getMessages() + sendMessage(Identity.nil(), server.getConfiguration().getMessages() .getMovedToNewServerPrefix().append(friendlyReason)); break; default: @@ -641,7 +641,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } else if (event.getResult() instanceof Notify) { Notify res = (Notify) event.getResult(); if (event.kickedDuringServerConnect() && previouslyConnected) { - sendMessage(res.getMessageComponent()); + sendMessage(Identity.nil(), res.getMessageComponent()); } else { disconnect(res.getMessageComponent()); } @@ -981,10 +981,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { switch (status.getStatus()) { case ALREADY_CONNECTED: - sendMessage(ConnectionMessages.ALREADY_CONNECTED); + sendMessage(Identity.nil(), ConnectionMessages.ALREADY_CONNECTED); break; case CONNECTION_IN_PROGRESS: - sendMessage(ConnectionMessages.IN_PROGRESS); + sendMessage(Identity.nil(), ConnectionMessages.IN_PROGRESS); break; case CONNECTION_CANCELLED: // Ignored; the plugin probably already handled this. From a76c01df4bba843efbf01e7985e7f51972fb573b Mon Sep 17 00:00:00 2001 From: Riley Park Date: Thu, 15 Oct 2020 08:24:41 -0700 Subject: [PATCH 21/39] Fix a few javadoc-related problems --- .../com/velocitypowered/api/command/CommandSource.java | 4 +++- .../main/java/com/velocitypowered/api/proxy/Player.java | 9 ++++++--- .../java/com/velocitypowered/api/proxy/ProxyServer.java | 8 ++++++-- .../com/velocitypowered/api/util/title/package-info.java | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) 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 346b21c27..2402976cd 100644 --- a/api/src/main/java/com/velocitypowered/api/command/CommandSource.java +++ b/api/src/main/java/com/velocitypowered/api/command/CommandSource.java @@ -3,6 +3,7 @@ package com.velocitypowered.api.command; import com.velocitypowered.api.permission.PermissionSubject; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.MessageType; +import net.kyori.adventure.identity.Identified; import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.legacytext3.LegacyText3ComponentSerializer; @@ -17,7 +18,8 @@ public interface CommandSource extends Audience, PermissionSubject { * Sends the specified {@code component} to the invoker. * * @param component the text component to send - * @deprecated Use {@link #sendMessage(Component)} instead + * @deprecated Use {@link #sendMessage(Identified, Component)} + * or {@link #sendMessage(Identity, Component)} instead */ @Deprecated void sendMessage(net.kyori.text.Component component); 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 3761843e2..598cb07a6 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Optional; import java.util.UUID; import net.kyori.adventure.identity.Identified; +import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; /** @@ -77,7 +78,8 @@ public interface Player extends CommandSource, Identified, InboundConnection, * Sends a chat message to the player's client. * * @param component the chat message to send - * @deprecated Use {@link #sendMessage(net.kyori.adventure.text.Component)} + * @deprecated Use {@link #sendMessage(Identified, Component)} + * or {@link #sendMessage(Identity, Component)} instead */ @Deprecated @Override @@ -90,8 +92,9 @@ public interface Player extends CommandSource, Identified, InboundConnection, * * @param component the chat message to send * @param position the position for the message - * @deprecated Use @deprecated Use {@link #sendMessage(net.kyori.adventure.text.Component)} or - * {@link #sendActionBar(net.kyori.adventure.text.Component)} + * @deprecated Use @deprecated Use {@link #sendMessage(Identified, Component)} or + * {@link #sendMessage(Identity, Component)} for chat messages, or + * {@link #sendActionBar(net.kyori.adventure.text.Component)} for action bar messages */ @Deprecated void sendMessage(net.kyori.text.Component component, MessagePosition position); diff --git a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java index d9ed1e4c8..f0b3876a0 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java @@ -18,6 +18,9 @@ import java.util.Collection; import java.util.Optional; import java.util.UUID; import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.identity.Identified; +import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.text.Component; import org.checkerframework.checker.nullness.qual.NonNull; /** @@ -26,7 +29,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; public interface ProxyServer extends Audience { /** - * Shuts down the proxy, kicking players with the specified {@param reason}. + * Shuts down the proxy, kicking players with the specified {@code reason}. * * @param reason message to kick online players with */ @@ -58,7 +61,8 @@ public interface ProxyServer extends Audience { * Broadcasts a message to all players currently online. * * @param component the message to send - * @deprecated Use {@link #sendMessage(net.kyori.adventure.text.Component)} instead + * @deprecated Use {@link #sendMessage(Identified, Component)} + * or {@link #sendMessage(Identity, Component)} instead */ @Deprecated void broadcast(net.kyori.text.Component component); diff --git a/api/src/main/java/com/velocitypowered/api/util/title/package-info.java b/api/src/main/java/com/velocitypowered/api/util/title/package-info.java index bedf52abb..e77c9e981 100644 --- a/api/src/main/java/com/velocitypowered/api/util/title/package-info.java +++ b/api/src/main/java/com/velocitypowered/api/util/title/package-info.java @@ -1,6 +1,6 @@ /** * Provides data structures for creating and manipulating titles. * - * @deprecated Replaced with {@link net.kyori.adventure.title} + * @deprecated Replaced with {@link net.kyori.adventure.title.Title} */ package com.velocitypowered.api.util.title; \ No newline at end of file From b75086aacb588dd5d15655c4870aece0131c125a Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 15 Oct 2020 11:46:44 -0400 Subject: [PATCH 22/39] Proper Minecraft 1.16.4-pre1 support. --- .../api/network/ProtocolVersion.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) 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 5a6b0f60b..c09583366 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -41,10 +41,14 @@ public enum ProtocolVersion { MINECRAFT_1_16_1(736, "1.16.1"), MINECRAFT_1_16_2(751, "1.16.2"), MINECRAFT_1_16_3(753, "1.16.3"), - MINECRAFT_1_16_4(754, "1.16.4"); + MINECRAFT_1_16_4(754, 1, "1.16.4"); + + private static final int SNAPSHOT_BIT = 30; private final int protocol; + private final int snapshotProtocol; private final String name; + private final boolean snapshot; /** * Represents the lowest supported version. @@ -70,6 +74,9 @@ public enum ProtocolVersion { Map versions = new HashMap<>(); for (ProtocolVersion version : values()) { versions.put(version.protocol, version); + if (version.snapshotProtocol != -1) { + versions.put(version.snapshotProtocol, version); + } } ID_TO_PROTOCOL_CONSTANT = ImmutableMap.copyOf(versions); @@ -93,8 +100,19 @@ public enum ProtocolVersion { } ProtocolVersion(int protocol, String name) { + this(protocol, -1, name); + } + + ProtocolVersion(int protocol, int snapshotProtocol, String name) { + if (snapshotProtocol != -1) { + this.snapshotProtocol = (1 << SNAPSHOT_BIT) | snapshotProtocol; + } else { + this.snapshotProtocol = -1; + } + this.protocol = protocol; this.name = name; + this.snapshot = true; } /** From 53b2400682a7d168fd75920d8e77447e225f7c71 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 15 Oct 2020 11:47:34 -0400 Subject: [PATCH 23/39] Remove bad snapshot flag. --- .../java/com/velocitypowered/api/network/ProtocolVersion.java | 2 -- 1 file changed, 2 deletions(-) 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 c09583366..9181e9bf8 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -48,7 +48,6 @@ public enum ProtocolVersion { private final int protocol; private final int snapshotProtocol; private final String name; - private final boolean snapshot; /** * Represents the lowest supported version. @@ -112,7 +111,6 @@ public enum ProtocolVersion { this.protocol = protocol; this.name = name; - this.snapshot = true; } /** From e20c37fcbac35b8d3d4906438c8aa1b36725d083 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 15 Oct 2020 15:38:51 -0400 Subject: [PATCH 24/39] Reintroduce two-packet respawn sequence for older clients Fixes #372 --- .../client/ClientPlaySessionHandler.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) 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 5717a0f05..c2f31eb98 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 @@ -1,6 +1,7 @@ package com.velocitypowered.proxy.connection.client; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.constructChannelsPacket; @@ -329,20 +330,18 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { // Most notably, by having the client accept the join game packet, we can work around the need // to perform entity ID rewrites, eliminating potential issues from rewriting packets and // improving compatibility with mods. - player.getConnection().delayedWrite(joinGame); - // Since 1.16 this dynamic changed: - // We don't need to send two dimension swiches anymore! - if (player.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_16) < 0) { - int tempDim = joinGame.getDimension() == 0 ? -1 : 0; - player.getConnection().delayedWrite( - new Respawn(tempDim, joinGame.getPartialHashedSeed(), joinGame.getDifficulty(), - joinGame.getGamemode(), joinGame.getLevelType(), - false, joinGame.getDimensionInfo(), joinGame.getPreviousGamemode(), - joinGame.getCurrentDimensionData())); + + int sentOldDim = joinGame.getDimension(); + if (player.getProtocolVersion().compareTo(MINECRAFT_1_16) < 0) { + // Before Minecraft 1.16, we could not switch to the same dimension without sending an + // additional respawn. On older versions of Minecraft this forces the client to perform + // garbage collection which adds additional latency. + joinGame.setDimension(joinGame.getDimension() == 0 ? -1 : 0); } + player.getConnection().delayedWrite(joinGame); player.getConnection().delayedWrite( - new Respawn(joinGame.getDimension(), joinGame.getPartialHashedSeed(), + new Respawn(sentOldDim, joinGame.getPartialHashedSeed(), joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType(), false, joinGame.getDimensionInfo(), joinGame.getPreviousGamemode(), joinGame.getCurrentDimensionData())); From fd6394517595e291e95bca5f60cb1a68bc18c8de Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 15 Oct 2020 15:57:27 -0400 Subject: [PATCH 25/39] Follow historical precedent with the new snapshot protocol release policy. --- .../com/velocitypowered/api/network/ProtocolVersion.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 9181e9bf8..d5bc745c2 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -72,7 +72,10 @@ public enum ProtocolVersion { static { Map versions = new HashMap<>(); for (ProtocolVersion version : values()) { - versions.put(version.protocol, version); + // For versions where the snapshot is compatible with the prior release version, Mojang will + // default to that. Follow that behavior since there is precedent (all the Minecraft 1.8 + // minor releases use the same protocol version). + versions.putIfAbsent(version.protocol, version); if (version.snapshotProtocol != -1) { versions.put(version.snapshotProtocol, version); } From be6f67d00282408e6195c00803241c6cbd8a302b Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 16 Oct 2020 17:24:37 -0400 Subject: [PATCH 26/39] Bump to Adventure 4.1.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ac901337f..a112be7ab 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ allprojects { ext { // dependency versions textVersion = '3.0.4' - adventureVersion = '4.0.0' + adventureVersion = '4.1.0' adventurePlatformVersion = '4.0.0-SNAPSHOT' junitVersion = '5.7.0' slf4jVersion = '1.7.30' From b78ffe6e86fd4d8379ff8be8322bbeac9344ee94 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 17 Oct 2020 00:32:28 -0400 Subject: [PATCH 27/39] Bump to Adventure 4.1.1 I don't even know --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a112be7ab..47f223b28 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ allprojects { ext { // dependency versions textVersion = '3.0.4' - adventureVersion = '4.1.0' + adventureVersion = '4.1.1' adventurePlatformVersion = '4.0.0-SNAPSHOT' junitVersion = '5.7.0' slf4jVersion = '1.7.30' From a6e708c98ee9b872cfc7866c74babe1ea908442d Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 20 Oct 2020 14:26:08 -0400 Subject: [PATCH 28/39] Fix Adventure sendActionBar implementation and add proper action bar sending for 1.16.2 --- .../proxy/connection/client/ConnectedPlayer.java | 13 ++++++++++--- .../velocitypowered/proxy/protocol/packet/Chat.java | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) 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 9672f3918..27f705c54 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 @@ -281,11 +281,18 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { @Override public void sendActionBar(net.kyori.adventure.text.@NonNull Component message) { - if (getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0) { + ProtocolVersion playerVersion = getProtocolVersion(); + if (playerVersion.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) { + // We do not need to use the title packets in 1.16.2+ + // https://bugs.mojang.com/browse/MC-119145 + Chat chat = Chat.createClientbound(Identity.nil(), message, getProtocolVersion()); + chat.setType(Chat.ACTION_TYPE); + connection.write(chat); + } else if (playerVersion.compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0) { // We can use the title packet instead. TitlePacket pkt = new TitlePacket(); pkt.setAction(TitlePacket.SET_ACTION_BAR); - pkt.setComponent(ProtocolUtils.getJsonChatSerializer(this.getProtocolVersion()) + pkt.setComponent(ProtocolUtils.getJsonChatSerializer(playerVersion) .serialize(message)); connection.write(pkt); } else { @@ -295,7 +302,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { object.addProperty("text", LegacyComponentSerializer.legacySection().serialize(message)); Chat chat = new Chat(); chat.setMessage(object.toString()); - chat.setType((byte) 1); + chat.setType(Chat.ACTION_TYPE); connection.write(chat); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java index 74f0a627d..8fbbc0f7f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java @@ -15,6 +15,7 @@ public class Chat implements MinecraftPacket { public static final byte CHAT_TYPE = (byte) 0; public static final byte SYSTEM_TYPE = (byte) 1; + public static final byte ACTION_TYPE = (byte) 2; public static final int MAX_SERVERBOUND_MESSAGE_LENGTH = 256; public static final UUID EMPTY_SENDER = new UUID(0, 0); From e3eace6a563f3b45d2b45e2781a125d942554b78 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 22 Oct 2020 00:14:41 -0400 Subject: [PATCH 29/39] Undeprecate CommandManager#register(String, Command, String...) This is a very convenient shorthand so let's save it from being axed. This is a change I made for an eventual Velocity 2.0.0 but it is backwards compatible for 1.1.0. --- .../java/com/velocitypowered/api/command/CommandManager.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/command/CommandManager.java b/api/src/main/java/com/velocitypowered/api/command/CommandManager.java index 68c3ed2c8..afe9356d5 100644 --- a/api/src/main/java/com/velocitypowered/api/command/CommandManager.java +++ b/api/src/main/java/com/velocitypowered/api/command/CommandManager.java @@ -46,9 +46,7 @@ public interface CommandManager { * @param command the command to register * @param otherAliases additional aliases * @throws IllegalArgumentException if one of the given aliases is already registered - * @deprecated Prefer {@link #register(CommandMeta, Command)} instead. */ - @Deprecated void register(String alias, Command command, String... otherAliases); /** From 8995b64dc6a102b03966dfa5baab0e0f06c79bb6 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 22 Oct 2020 03:02:09 -0400 Subject: [PATCH 30/39] Do not use the GAME_INFO chat type. --- .../proxy/connection/client/ConnectedPlayer.java | 12 +++--------- .../velocitypowered/proxy/protocol/packet/Chat.java | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) 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 27f705c54..2cad65f05 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 @@ -282,14 +282,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { @Override public void sendActionBar(net.kyori.adventure.text.@NonNull Component message) { ProtocolVersion playerVersion = getProtocolVersion(); - if (playerVersion.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) { - // We do not need to use the title packets in 1.16.2+ - // https://bugs.mojang.com/browse/MC-119145 - Chat chat = Chat.createClientbound(Identity.nil(), message, getProtocolVersion()); - chat.setType(Chat.ACTION_TYPE); - connection.write(chat); - } else if (playerVersion.compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0) { - // We can use the title packet instead. + if (playerVersion.compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0) { + // Use the title packet instead. TitlePacket pkt = new TitlePacket(); pkt.setAction(TitlePacket.SET_ACTION_BAR); pkt.setComponent(ProtocolUtils.getJsonChatSerializer(playerVersion) @@ -302,7 +296,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { object.addProperty("text", LegacyComponentSerializer.legacySection().serialize(message)); Chat chat = new Chat(); chat.setMessage(object.toString()); - chat.setType(Chat.ACTION_TYPE); + chat.setType(Chat.GAME_INFO_TYPE); connection.write(chat); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java index 8fbbc0f7f..fd64ea600 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java @@ -15,7 +15,7 @@ public class Chat implements MinecraftPacket { public static final byte CHAT_TYPE = (byte) 0; public static final byte SYSTEM_TYPE = (byte) 1; - public static final byte ACTION_TYPE = (byte) 2; + public static final byte GAME_INFO_TYPE = (byte) 2; public static final int MAX_SERVERBOUND_MESSAGE_LENGTH = 256; public static final UUID EMPTY_SENDER = new UUID(0, 0); From cfb9104696d08559f22f8e18fa01a60628dcf138 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 22 Oct 2020 12:42:35 -0400 Subject: [PATCH 31/39] 1.16.4-pre2 support --- .../java/com/velocitypowered/api/network/ProtocolVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d5bc745c2..830911c0e 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -41,7 +41,7 @@ public enum ProtocolVersion { MINECRAFT_1_16_1(736, "1.16.1"), MINECRAFT_1_16_2(751, "1.16.2"), MINECRAFT_1_16_3(753, "1.16.3"), - MINECRAFT_1_16_4(754, 1, "1.16.4"); + MINECRAFT_1_16_4(754, 2, "1.16.4"); private static final int SNAPSHOT_BIT = 30; From 278930a0083707de2b64080f2f9e252844de339a Mon Sep 17 00:00:00 2001 From: A248 Date: Mon, 26 Oct 2020 13:52:04 -0400 Subject: [PATCH 32/39] Handle exceptions relating to CompletableFuture operations Solves #374 --- .../velocitypowered/proxy/VelocityServer.java | 5 ++- .../backend/BackendPlaySessionHandler.java | 12 +++++-- .../client/ClientPlaySessionHandler.java | 36 ++++++++++++++++--- .../connection/client/ConnectedPlayer.java | 8 ++++- .../client/LoginSessionHandler.java | 22 ++++++++++-- .../client/StatusSessionHandler.java | 12 +++++-- .../proxy/plugin/VelocityEventManager.java | 8 ++--- .../proxy/protocol/netty/GS4QueryHandler.java | 8 ++++- 8 files changed, 91 insertions(+), 20 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 54c446a88..863a481c7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -425,8 +425,11 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { .toArray((IntFunction[]>) CompletableFuture[]::new)); playersTeardownFuture.get(10, TimeUnit.SECONDS); - } catch (TimeoutException | ExecutionException e) { + } catch (TimeoutException e) { timedOut = true; + } catch (ExecutionException e) { + timedOut = true; + logger.error("Exception while tearing down player connections", e); } eventManager.fireShutdownEvent(); 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 bbba322d9..abd1f7b91 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 @@ -149,7 +149,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { Unpooled.wrappedBuffer(copy)); playerConnection.write(copied); } - }, playerConnection.eventLoop()); + }, playerConnection.eventLoop()) + .exceptionally((ex) -> { + logger.error("Exception while handling plugin message {}", packet, ex); + return null; + }); return true; } @@ -186,7 +190,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { server.getEventManager().fire( new PlayerAvailableCommandsEvent(serverConn.getPlayer(), rootNode)) - .thenAcceptAsync(event -> playerConnection.write(commands), playerConnection.eventLoop()); + .thenAcceptAsync(event -> playerConnection.write(commands), playerConnection.eventLoop()) + .exceptionally((ex) -> { + logger.error("Exception while handling available commands for {}", playerConnection, ex); + return null; + }); return true; } 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 c2f31eb98..956772000 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 @@ -157,7 +157,11 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { smc.write(packet); } } - }, smc.eventLoop()); + }, smc.eventLoop()) + .exceptionally((ex) -> { + logger.error("Exception while handling player chat for {}", player, ex); + return null; + }); } return true; } @@ -225,7 +229,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { Unpooled.wrappedBuffer(copy)); backendConn.write(message); } - }, backendConn.eventLoop()); + }, backendConn.eventLoop()) + .exceptionally((ex) -> { + logger.error("Exception while handling plugin message packet for {}", + player, ex); + return null; + }); } } } @@ -423,7 +432,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { resp.getOffers().addAll(offers); player.getConnection().write(resp); } - }, player.getConnection().eventLoop()); + }, player.getConnection().eventLoop()) + .exceptionally((ex) -> { + logger.error("Exception while handling command tab completion for player {} executing {}", + player, command, ex); + return null; + }); return true; // Sorry, handler; we're just gonna have to lie to you here. } @@ -475,7 +489,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { player.getUsername(), command, e); } - }, player.getConnection().eventLoop()); + }, player.getConnection().eventLoop()) + .exceptionally((ex) -> { + logger.error( + "Exception while finishing command tab completion, with request {} and response {}", + request, response, ex); + return null; + }); } private void finishRegularTabComplete(TabCompleteRequest request, TabCompleteResponse response) { @@ -490,7 +510,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { response.getOffers().add(new Offer(s)); } player.getConnection().write(response); - }, player.getConnection().eventLoop()); + }, player.getConnection().eventLoop()) + .exceptionally((ex) -> { + logger.error( + "Exception while finishing regular tab completion, with request {} and response{}", + request, response, ex); + return null; + }); } private CompletableFuture processCommandExecuteResult(String originalCommand, 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 2cad65f05..f635babf5 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 @@ -757,7 +757,13 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } DisconnectEvent event = new DisconnectEvent(this, status); - server.getEventManager().fire(event).thenRun(() -> this.teardownFuture.complete(null)); + server.getEventManager().fire(event).whenComplete((val, ex) -> { + if (ex == null) { + this.teardownFuture.complete(null); + } else { + this.teardownFuture.completeExceptionally(ex); + } + }); } public CompletableFuture getTeardownFuture() { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java index 826baec66..fbfca874f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java @@ -183,7 +183,11 @@ public class LoginSessionHandler implements MinecraftSessionHandler { } else { initializePlayer(GameProfile.forOfflinePlayer(login.getUsername()), false); } - }, mcConnection.eventLoop()); + }, mcConnection.eventLoop()) + .exceptionally((ex) -> { + logger.error("Exception in pre-login stage", ex); + return null; + }); } private EncryptionRequest generateEncryptionRequest() { @@ -202,6 +206,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { server.getConfiguration().getPlayerInfoForwardingMode()); GameProfileRequestEvent profileRequestEvent = new GameProfileRequestEvent(inbound, profile, onlineMode); + final GameProfile finalProfile = profile; server.getEventManager().fire(profileRequestEvent).thenCompose(profileEvent -> { if (mcConnection.isClosed()) { @@ -229,6 +234,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler { completeLoginProtocolPhaseAndInitialize(player); } }, mcConnection.eventLoop()); + }).exceptionally((ex) -> { + logger.error("Exception during connection of {}", finalProfile, ex); + return null; }); } @@ -274,7 +282,11 @@ public class LoginSessionHandler implements MinecraftSessionHandler { server.getEventManager().fire(new PostLoginEvent(player)) .thenRun(() -> connectToInitialServer(player)); } - }, mcConnection.eventLoop()); + }, mcConnection.eventLoop()) + .exceptionally((ex) -> { + logger.error("Exception while completing login initialisation phase for {}", player, ex); + return null; + }); } private void connectToInitialServer(ConnectedPlayer player) { @@ -291,7 +303,11 @@ public class LoginSessionHandler implements MinecraftSessionHandler { return; } player.createConnectionRequest(toTry.get()).fireAndForget(); - }, mcConnection.eventLoop()); + }, mcConnection.eventLoop()) + .exceptionally((ex) -> { + logger.error("Exception while connecting {} to initial server", player, ex); + return null; + }); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java index cffc71377..2b44c654b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java @@ -163,7 +163,11 @@ public class StatusSessionHandler implements MinecraftSessionHandler { .thenCompose(ping -> server.getEventManager().fire(new ProxyPingEvent(inbound, ping))) .thenAcceptAsync(event -> connection.closeWith( LegacyDisconnect.fromServerPing(event.getPing(), packet.getVersion())), - connection.eventLoop()); + connection.eventLoop()) + .exceptionally((ex) -> { + logger.error("Exception while handling legacy ping {}", packet, ex); + return null; + }); return true; } @@ -189,7 +193,11 @@ public class StatusSessionHandler implements MinecraftSessionHandler { .toJson(event.getPing(), json); connection.write(new StatusResponse(json)); }, - connection.eventLoop()); + connection.eventLoop()) + .exceptionally((ex) -> { + logger.error("Exception while handling status request {}", packet, ex); + return null; + }); return true; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java index 8d9b40bbf..5d0d58551 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java @@ -115,12 +115,10 @@ public class VelocityEventManager implements EventManager { return CompletableFuture.completedFuture(event); } - CompletableFuture eventFuture = new CompletableFuture<>(); - service.execute(() -> { + return CompletableFuture.supplyAsync(() -> { fireEvent(event); - eventFuture.complete(event); - }); - return eventFuture; + return event; + }, service); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java index 888f6ab29..ffe2a24b4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java @@ -29,6 +29,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; +import org.apache.logging.log4j.LogManager; public class GS4QueryHandler extends SimpleChannelInboundHandler { @@ -162,7 +163,12 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler // Send the response DatagramPacket responsePacket = new DatagramPacket(queryResponse, msg.sender()); ctx.writeAndFlush(responsePacket, ctx.voidPromise()); - }, ctx.channel().eventLoop()); + }, ctx.channel().eventLoop()) + .exceptionally((ex) -> { + LogManager.getLogger(getClass()).error( + "Exception while writing GS4 response for query from {}", senderAddress, ex); + return null; + }); break; } default: From 8fbce8423f3e714f4e68cac7c9da071538643dfd Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 26 Oct 2020 17:20:52 -0400 Subject: [PATCH 33/39] Fix typo in ServerPing#asBuilder() Javadoc Fixes #375 --- .../com/velocitypowered/api/proxy/server/ServerPing.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java b/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java index f1979d491..dcb44f7ff 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java @@ -133,8 +133,9 @@ public final class ServerPing { /** * Returns a copy of this {@link ServerPing} instance as a builder so that it can be modified. - * It is guaranteed that {@code ping.asBuilder().ping().equals(ping)}: that is, if no other - * changes are made to the returned builder, the built instance will equal the original instance. + * It is guaranteed that {@code ping.asBuilder().build().equals(ping)} is true: that is, if no + * other changes are made to the returned builder, the built instance will equal the original + * instance. * * @return a copy of this instance as a {@link Builder} */ From 6331e1af3ef9a848cb654ce8c63b782c3a36e10a Mon Sep 17 00:00:00 2001 From: "Five (Xer)" Date: Fri, 23 Oct 2020 18:08:02 +0200 Subject: [PATCH 34/39] Velocity Dump WIP --- .../command/builtin/VelocityCommand.java | 49 ++++++ .../proxy/config/VelocityConfiguration.java | 55 ++++--- .../proxy/util/InformationUtils.java | 154 ++++++++++++++++++ 3 files changed, 231 insertions(+), 27 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java 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 02c085241..52aa7468a 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 @@ -3,19 +3,27 @@ package com.velocitypowered.proxy.command.builtin; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; + +import com.google.common.collect.ImmutableSet; +import com.google.gson.JsonObject; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.SimpleCommand; import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.ProxyVersion; import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.util.InformationUtils; + import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; + import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; @@ -52,6 +60,7 @@ public class VelocityCommand implements SimpleCommand { .put("version", new Info(server)) .put("plugins", new Plugins(server)) .put("reload", new Reload(server)) + .put("dump", new Dump(server)) .build(); } @@ -289,4 +298,44 @@ public class VelocityCommand implements SimpleCommand { return source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE; } } + + private static class Dump implements SubCommand { + + private final ProxyServer server; + + private Dump(ProxyServer server) { + this.server = server; + } + + @Override + public void execute(CommandSource source, String @NonNull [] args) { + if (args.length != 0) { + source.sendMessage(Identity.nil(), Component.text("/velocity dump", NamedTextColor.RED)); + return; + } + + Collection allServers = ImmutableSet.copyOf(server.getAllServers()); + JsonObject servers = new JsonObject(); + for (RegisteredServer iter : allServers) { + servers.add(iter.getServerInfo().getName(), + InformationUtils.collectServerInfo(iter)); + } + + JsonObject proxyConfig = InformationUtils.collectProxyConfig(server.getConfiguration()); + proxyConfig.add("servers", servers); + + JsonObject dump = new JsonObject(); + dump.add("versionInfo", InformationUtils.collectProxyInfo(server.getVersion())); + dump.add("platform", InformationUtils.collectEnvironmentInfo()); + dump.add("config", proxyConfig); + dump.add("plugins", InformationUtils.collectPluginInfo(server)); + + // TODO: Finish + } + + @Override + public boolean hasPermission(final CommandSource source, final String @NonNull [] args) { + return source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE; + } + } } 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 a3f7537f2..5660b913e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -7,6 +7,7 @@ import com.electronwill.nightconfig.toml.TomlFormat; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.gson.annotations.Expose; import com.velocitypowered.api.proxy.config.ProxyConfig; import com.velocitypowered.api.util.Favicon; import com.velocitypowered.proxy.util.AddressUtil; @@ -42,20 +43,20 @@ public class VelocityConfiguration implements ProxyConfig { private static final Logger logger = LogManager.getLogger(VelocityConfiguration.class); - private String bind = "0.0.0.0:25577"; - private String motd = "&3A Velocity Server"; - private int showMaxPlayers = 500; - private boolean onlineMode = true; - private boolean preventClientProxyConnections = false; - private PlayerInfoForwarding playerInfoForwardingMode = PlayerInfoForwarding.NONE; + @Expose private String bind = "0.0.0.0:25577"; + @Expose private String motd = "&3A Velocity Server"; + @Expose private int showMaxPlayers = 500; + @Expose private boolean onlineMode = true; + @Expose private boolean preventClientProxyConnections = false; + @Expose private PlayerInfoForwarding playerInfoForwardingMode = PlayerInfoForwarding.NONE; private byte[] forwardingSecret = generateRandomString(12).getBytes(StandardCharsets.UTF_8); - private boolean announceForge = false; - private boolean onlineModeKickExistingPlayers = false; - private PingPassthroughMode pingPassthrough = PingPassthroughMode.DISABLED; + @Expose private boolean announceForge = false; + @Expose private boolean onlineModeKickExistingPlayers = false; + @Expose private PingPassthroughMode pingPassthrough = PingPassthroughMode.DISABLED; private final Servers servers; private final ForcedHosts forcedHosts; - private final Advanced advanced; - private final Query query; + @Expose private final Advanced advanced; + @Expose private final Query query; private final Metrics metrics; private final Messages messages; private net.kyori.adventure.text.@MonotonicNonNull Component motdAsComponent; @@ -622,18 +623,18 @@ public class VelocityConfiguration implements ProxyConfig { private static class Advanced { - private int compressionThreshold = 256; - private int compressionLevel = -1; - private int loginRatelimit = 3000; - private int connectionTimeout = 5000; - private int readTimeout = 30000; - private boolean proxyProtocol = false; - private boolean tcpFastOpen = false; - private boolean bungeePluginMessageChannel = true; - private boolean showPingRequests = false; - private boolean failoverOnUnexpectedServerDisconnect = true; - private boolean announceProxyCommands = true; - private boolean logCommandExecutions = false; + @Expose private int compressionThreshold = 256; + @Expose private int compressionLevel = -1; + @Expose private int loginRatelimit = 3000; + @Expose private int connectionTimeout = 5000; + @Expose private int readTimeout = 30000; + @Expose private boolean proxyProtocol = false; + @Expose private boolean tcpFastOpen = false; + @Expose private boolean bungeePluginMessageChannel = true; + @Expose private boolean showPingRequests = false; + @Expose private boolean failoverOnUnexpectedServerDisconnect = true; + @Expose private boolean announceProxyCommands = true; + @Expose private boolean logCommandExecutions = false; private Advanced() { } @@ -725,10 +726,10 @@ public class VelocityConfiguration implements ProxyConfig { private static class Query { - private boolean queryEnabled = false; - private int queryPort = 25577; - private String queryMap = "Velocity"; - private boolean showPlugins = false; + @Expose private boolean queryEnabled = false; + @Expose private int queryPort = 25577; + @Expose private String queryMap = "Velocity"; + @Expose private boolean showPlugins = false; private Query() { } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java new file mode 100644 index 000000000..ebc1dd015 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java @@ -0,0 +1,154 @@ +package com.velocitypowered.proxy.util; + +import com.google.common.collect.ImmutableList; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.velocitypowered.api.plugin.PluginContainer; +import com.velocitypowered.api.plugin.PluginDescription; +import com.velocitypowered.api.plugin.meta.PluginDependency; +import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.config.ProxyConfig; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.util.ProxyVersion; + +import io.netty.channel.unix.DomainSocketAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.List; +import joptsimple.internal.Strings; + +public enum InformationUtils { + ; + + /** + * Retrieves a {@link JsonArray} containing basic information about all + * running plugins on the {@link ProxyServer} instance. + * + * @param proxy the proxy instance to retrieve from + * @return {@link JsonArray} containing zero or more {@link JsonObject} + */ + public static JsonArray collectPluginInfo(ProxyServer proxy) { + List allPlugins = ImmutableList.copyOf( + proxy.getPluginManager().getPlugins()); + JsonArray plugins = new JsonArray(); + + for (PluginContainer plugin : allPlugins) { + PluginDescription desc = plugin.getDescription(); + JsonObject current = new JsonObject(); + current.addProperty("id", desc.getId()); + if (desc.getName().isPresent()) { + current.addProperty("name", desc.getName().get()); + } + if (desc.getVersion().isPresent()) { + current.addProperty("version", desc.getVersion().get()); + } + if (!desc.getAuthors().isEmpty()) { + current.addProperty("authors", + Strings.join(desc.getAuthors(), ",")); + } + if (desc.getDescription().isPresent()) { + current.addProperty("description", desc.getDescription().get()); + } + if (desc.getUrl().isPresent()) { + current.addProperty("url", desc.getUrl().get()); + } + if (!desc.getDependencies().isEmpty()) { + JsonArray dependencies = new JsonArray(); + for (PluginDependency dependency : desc.getDependencies()) { + dependencies.add(dependency.getId()); + } + current.add("dependencies", dependencies); + } + plugins.add(current); + } + return plugins; + } + + /** + * Creates a {@link JsonObject} containing information about the + * current environment the project is run under. + * + * @return {@link JsonObject} containing environment info + */ + public static JsonObject collectEnvironmentInfo() { + JsonObject envInfo = new JsonObject(); + envInfo.addProperty("operatingSystemType", System.getProperty("os.name")); + envInfo.addProperty("operatingSystemVersion", System.getProperty("os.version")); + envInfo.addProperty("operatingSystemArchitecture", System.getProperty("os.arch")); + envInfo.addProperty("javaVersion", System.getProperty("java.version")); + envInfo.addProperty("javaVendor", System.getProperty("java.vendor")); + return envInfo; + } + + /** + * Creates a {@link JsonObject} containing most relevant + * information of the {@link RegisteredServer} for diagnosis. + * + * @param server the server to evaluate + * @return {@link JsonObject} containing server and diagnostic info + */ + public static JsonObject collectServerInfo(RegisteredServer server) { + JsonObject info = new JsonObject(); + info.addProperty("currentPlayers", server.getPlayersConnected().size()); + SocketAddress address = server.getServerInfo().getAddress(); + if (address instanceof InetSocketAddress) { + InetSocketAddress iaddr = (InetSocketAddress) address; + info.addProperty("socketType", "EventLoop"); + info.addProperty("unresolved", iaddr.isUnresolved()); + // Greetings form Netty 4aa10db9 + info.addProperty("host", iaddr.getHostString()); + info.addProperty("port", iaddr.getPort()); + } else if (address instanceof DomainSocketAddress) { + DomainSocketAddress daddr = (DomainSocketAddress) address; + info.addProperty("socketType", "Unix/Epoll"); + info.addProperty("host", daddr.path()); + } else { + info.addProperty("socketType", "Unknown/Generic"); + info.addProperty("host", address.toString()); + } + return info; + } + + /** + * Creates a {@link JsonObject} containing information about the + * current environment the project is run under. + * + * @param version the proxy instance to retrieve from + * @return {@link JsonObject} containing environment info + */ + public static JsonObject collectProxyInfo(ProxyVersion version) { + return (JsonObject) serializeObject(version, false); + } + + /** + * Creates a {@link JsonObject} containing most relevant + * information of the {@link ProxyConfig} for diagnosis. + * + * @param config the config instance to retrieve from + * @return {@link JsonObject} containing select config values + */ + public static JsonObject collectProxyConfig(ProxyConfig config) { + return (JsonObject) serializeObject(config, true); + } + + private static JsonElement serializeObject(Object toSerialize, boolean withExcludes) { + return JsonParser.parseString( + withExcludes ? GSON_WITH_EXCLUDES.toJson(toSerialize) : + GSON_WITHOUT_EXCLUDES.toJson(toSerialize)); + } + + private static final Gson GSON_WITH_EXCLUDES = new GsonBuilder() + .setPrettyPrinting() + .excludeFieldsWithoutExposeAnnotation() + .create(); + + private static final Gson GSON_WITHOUT_EXCLUDES = new GsonBuilder() + .setPrettyPrinting() + .create(); + + +} From 140eaaf5abe79270e6cf3bac25a8182524c27b8c Mon Sep 17 00:00:00 2001 From: "Five (Xer)" Date: Sun, 25 Oct 2020 20:04:52 +0100 Subject: [PATCH 35/39] Velocity Dump WIP Part 2 --- .../command/builtin/VelocityCommand.java | 104 ++++++++++++++++- .../proxy/util/InformationUtils.java | 110 +++++++++++++++++- 2 files changed, 208 insertions(+), 6 deletions(-) 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 52aa7468a..08ce75991 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 @@ -5,7 +5,11 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.io.CharStreams; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.SimpleCommand; import com.velocitypowered.api.permission.Tristate; @@ -17,6 +21,12 @@ import com.velocitypowered.api.util.ProxyVersion; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.util.InformationUtils; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -320,9 +330,18 @@ public class VelocityCommand implements SimpleCommand { servers.add(iter.getServerInfo().getName(), InformationUtils.collectServerInfo(iter)); } + JsonArray connectOrder = new JsonArray(); + List attemptedConnectionOrder = ImmutableList.copyOf( + server.getConfiguration().getAttemptConnectionOrder()); + for (int i = 0; i < attemptedConnectionOrder.size(); i++) { + connectOrder.add(attemptedConnectionOrder.get(i)); + } JsonObject proxyConfig = InformationUtils.collectProxyConfig(server.getConfiguration()); proxyConfig.add("servers", servers); + proxyConfig.add("connectOrder", connectOrder); + proxyConfig.add("forcedHosts", + InformationUtils.collectForcedHosts(server.getConfiguration())); JsonObject dump = new JsonObject(); dump.add("versionInfo", InformationUtils.collectProxyInfo(server.getVersion())); @@ -330,7 +349,90 @@ public class VelocityCommand implements SimpleCommand { dump.add("config", proxyConfig); dump.add("plugins", InformationUtils.collectPluginInfo(server)); - // TODO: Finish + source.sendMessage(Component.text().content("Uploading gathered information...").build()); + + HttpURLConnection upload = null; + try { + upload = (HttpURLConnection) new URL("https://dump.velocitypowered.com/documents") + .openConnection(); + } catch (IOException e1) { + // Couldn't open connection; + source.sendMessage( + Component.text() + .content("Failed to open a connection!") + .color(NamedTextColor.RED).build()); + return; + } + upload.setRequestProperty("Content-Type", "text/plain"); + upload.addRequestProperty( + "User-Agent", server.getVersion().getName() + "/" + + server.getVersion().getVersion()); + try { + upload.setRequestMethod("POST"); + upload.setDoOutput(true); + + OutputStream uploadStream = upload.getOutputStream(); + uploadStream.write( + InformationUtils.toHumanReadableString(dump).getBytes(StandardCharsets.UTF_8)); + uploadStream.close(); + } catch (IOException e2) { + // Couldn't POST the Data + source.sendMessage( + Component.text() + .content("Couldn't upload the data!") + .color(NamedTextColor.RED).build()); + return; + } + String rawResponse = null; + try { + rawResponse = CharStreams.toString( + new InputStreamReader(upload.getInputStream(), StandardCharsets.UTF_8)); + upload.getInputStream().close(); + } catch (IOException e3) { + // Couldn't read response + source.sendMessage( + Component.text() + .content("Invalid server response received!") + .color(NamedTextColor.RED).build()); + } + JsonObject returned = null; + try { + returned = InformationUtils.parseString(rawResponse); + if (returned == null || !returned.has("key")) { + throw new JsonParseException("Invalid json response"); + } + } catch (JsonSyntaxException e4) { + // Mangled json + source.sendMessage( + Component.text() + .content("Server responded with invalid data!") + .color(NamedTextColor.RED).build()); + return; + } catch (JsonParseException e5) { + // Backend error? + source.sendMessage( + Component.text() + .content("Data was uploaded successfully but couldn't be posted") + .color(NamedTextColor.RED).build()); + return; + } + TextComponent response = Component.text() + .content("Created an anonymised report containing useful information about") + .append(Component.newline() + .append( + Component.text("this proxy. If a developer requested it" + + ", you may share the")) + .append(Component.newline()) + .append(Component.text("following link with them:")) + .append(Component.newline()) + .append(Component.text("https://dump.velocitypowered.com/" + + returned.get("key").getAsString() + ".json") + .color(NamedTextColor.GREEN))) + .append(Component.newline()) + .append(Component.text("Note: This link is only valid for a few days")) + .build(); + source.sendMessage(response); + } @Override 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 ebc1dd015..9016ae3ff 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java @@ -1,6 +1,7 @@ package com.velocitypowered.proxy.util; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; @@ -16,9 +17,15 @@ import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.ProxyVersion; import io.netty.channel.unix.DomainSocketAddress; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.List; +import java.util.Map; + import joptsimple.internal.Strings; public enum InformationUtils { @@ -84,6 +91,75 @@ public enum InformationUtils { return envInfo; } + /** + * Creates a {@link JsonObject} containing information about the + * forced hosts of the {@link ProxyConfig} instance. + * + * @return {@link JsonArray} containing forced hosts + */ + public static JsonObject collectForcedHosts(ProxyConfig config) { + JsonObject forcedHosts = new JsonObject(); + Map> allForcedHosts = ImmutableMap.copyOf( + config.getForcedHosts()); + for (Map.Entry> entry : allForcedHosts.entrySet()) { + JsonArray host = new JsonArray(); + for (int i = 0; i < entry.getValue().size(); i++) { + host.add(entry.getValue().get(i)); + } + forcedHosts.add(entry.getKey(), host); + } + return forcedHosts; + } + + /** + * Anonymises or redacts a given {@link InetAddress} + * public address bits. + * + * @param address The address to redact + * @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 ret; + } + } else { + return address.getHostAddress(); + } + } + /** * Creates a {@link JsonObject} containing most relevant * information of the {@link RegisteredServer} for diagnosis. @@ -97,18 +173,22 @@ public enum InformationUtils { SocketAddress address = server.getServerInfo().getAddress(); if (address instanceof InetSocketAddress) { InetSocketAddress iaddr = (InetSocketAddress) address; - info.addProperty("socketType", "EventLoop"); + info.addProperty("socketType", "EventLoop/NIO"); info.addProperty("unresolved", iaddr.isUnresolved()); - // Greetings form Netty 4aa10db9 - info.addProperty("host", iaddr.getHostString()); + if (iaddr.isUnresolved()) { + // Greetings form Netty 4aa10db9 + info.addProperty("host", iaddr.getHostString()); + } else { + info.addProperty("host", anonymizeInetAddress(iaddr.getAddress())); + } info.addProperty("port", iaddr.getPort()); } else if (address instanceof DomainSocketAddress) { DomainSocketAddress daddr = (DomainSocketAddress) address; info.addProperty("socketType", "Unix/Epoll"); - info.addProperty("host", daddr.path()); + info.addProperty("path", daddr.path()); } else { info.addProperty("socketType", "Unknown/Generic"); - info.addProperty("host", address.toString()); + info.addProperty("info", address.toString()); } return info; } @@ -135,6 +215,26 @@ public enum InformationUtils { return (JsonObject) serializeObject(config, true); } + /** + * Creates a human-readable String from a {@link JsonElement}. + * + * @param json the {@link JsonElement} object + * @return the human-readable String + */ + public static String toHumanReadableString(JsonElement json) { + return GSON_WITHOUT_EXCLUDES.toJson(json); + } + + /** + * Creates a {@link JsonObject} from a String. + * + * @param toParse the String to parse + * @return {@link JsonObject} object + */ + public static JsonObject parseString(String toParse) { + return GSON_WITHOUT_EXCLUDES.fromJson(toParse, JsonObject.class); + } + private static JsonElement serializeObject(Object toSerialize, boolean withExcludes) { return JsonParser.parseString( withExcludes ? GSON_WITH_EXCLUDES.toJson(toSerialize) : From 01070f8fd27643d229a16d1472b75f672cb33d82 Mon Sep 17 00:00:00 2001 From: "Five (Xer)" Date: Tue, 27 Oct 2020 01:09:43 +0100 Subject: [PATCH 36/39] Velocity Dump Cleanup --- .../command/builtin/VelocityCommand.java | 178 +++++++++--------- .../proxy/util/InformationUtils.java | 4 - 2 files changed, 92 insertions(+), 90 deletions(-) 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 08ce75991..3218960b9 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 @@ -5,7 +5,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.io.CharStreams; +import com.google.common.util.concurrent.MoreExecutors; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; @@ -21,17 +21,15 @@ import com.velocitypowered.api.util.ProxyVersion; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.util.InformationUtils; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; +import java.net.ConnectException; +import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import net.kyori.adventure.identity.Identity; @@ -43,6 +41,10 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.asynchttpclient.AsyncHttpClient; +import org.asynchttpclient.BoundRequestBuilder; +import org.asynchttpclient.ListenableFuture; +import org.asynchttpclient.Response; import org.checkerframework.checker.nullness.qual.NonNull; public class VelocityCommand implements SimpleCommand { @@ -311,6 +313,7 @@ public class VelocityCommand implements SimpleCommand { private static class Dump implements SubCommand { + private static final Logger logger = LogManager.getLogger(Dump.class); private final ProxyServer server; private Dump(ProxyServer server) { @@ -350,89 +353,92 @@ public class VelocityCommand implements SimpleCommand { dump.add("plugins", InformationUtils.collectPluginInfo(server)); source.sendMessage(Component.text().content("Uploading gathered information...").build()); + AsyncHttpClient httpClient = ((VelocityServer) server).getAsyncHttpClient(); - HttpURLConnection upload = null; - try { - upload = (HttpURLConnection) new URL("https://dump.velocitypowered.com/documents") - .openConnection(); - } catch (IOException e1) { - // Couldn't open connection; - source.sendMessage( - Component.text() - .content("Failed to open a connection!") - .color(NamedTextColor.RED).build()); - return; - } - upload.setRequestProperty("Content-Type", "text/plain"); - upload.addRequestProperty( - "User-Agent", server.getVersion().getName() + "/" - + server.getVersion().getVersion()); - try { - upload.setRequestMethod("POST"); - upload.setDoOutput(true); + BoundRequestBuilder request = + httpClient.preparePost("https://dump.velocitypowered.com/documents"); + request.setHeader("Content-Type", "text/plain"); + request.addHeader("User-Agent", server.getVersion().getName() + "/" + + server.getVersion().getVersion()); + request.setBody( + InformationUtils.toHumanReadableString(dump).getBytes(StandardCharsets.UTF_8)); - OutputStream uploadStream = upload.getOutputStream(); - uploadStream.write( - InformationUtils.toHumanReadableString(dump).getBytes(StandardCharsets.UTF_8)); - uploadStream.close(); - } catch (IOException e2) { - // Couldn't POST the Data - source.sendMessage( - Component.text() - .content("Couldn't upload the data!") - .color(NamedTextColor.RED).build()); - return; - } - String rawResponse = null; - try { - rawResponse = CharStreams.toString( - new InputStreamReader(upload.getInputStream(), StandardCharsets.UTF_8)); - upload.getInputStream().close(); - } catch (IOException e3) { - // Couldn't read response - source.sendMessage( - Component.text() - .content("Invalid server response received!") - .color(NamedTextColor.RED).build()); - } - JsonObject returned = null; - try { - returned = InformationUtils.parseString(rawResponse); - if (returned == null || !returned.has("key")) { - throw new JsonParseException("Invalid json response"); + ListenableFuture future = request.execute(); + future.addListener(() -> { + try { + Response response = future.get(); + if (response.getStatusCode() != 200) { + source.sendMessage(Component.text() + .content("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.") + .color(NamedTextColor.RED).build()); + logger.error("Invalid status code while POST-ing Velocity dump: " + + response.getStatusCode()); + logger.error("Headers: \n--------------BEGIN HEADERS--------------\n" + + response.getHeaders().toString() + + "\n---------------END HEADERS---------------"); + return; + } + JsonObject key = InformationUtils.parseString( + response.getResponseBody(StandardCharsets.UTF_8)); + if (!key.has("key")) { + throw new JsonSyntaxException("Missing Dump-Url-response"); + } + String url = "https://dump.velocitypowered.com/" + + key.get("key").getAsString() + ".json"; + source.sendMessage(Component.text() + .content("Created an anonymised report containing useful information about " + + "this proxy. If a developer requested it, you may share the " + + "following link with them:") + .append(Component.newline()) + .append(Component.text(">> " + url) + .color(NamedTextColor.GREEN) + .clickEvent(ClickEvent.openUrl(url))) + .append(Component.newline()) + .append(Component.text("Note: This link is only valid for a few days") + .color(NamedTextColor.GRAY) + ).build()); + } catch (InterruptedException e) { + source.sendMessage(Component.text() + .content("Could not complete the request, the command was interrupted." + + "Please refer to the proxy-log or console for more information.") + .color(NamedTextColor.RED).build()); + logger.error("Failed to complete dump command, " + + "the executor was interrupted: " + e.getMessage()); + e.printStackTrace(); + } catch (ExecutionException e) { + TextComponent.Builder message = Component.text() + .content("An error occurred while attempting to upload the gathered " + + "information to the Velocity servers.") + .append(Component.newline()) + .color(NamedTextColor.RED); + if (e.getCause() instanceof UnknownHostException + || e.getCause() instanceof ConnectException) { + message.append(Component.text( + "Likely cause: Invalid system DNS settings or no internet connection")); + } + source.sendMessage(message + .append(Component.newline() + .append(Component.text( + "Error details can be found in the proxy log / console")) + ).build()); + + logger.error("Failed to complete dump command, " + + "the executor encountered an Exception: " + e.getCause().getMessage()); + e.getCause().printStackTrace(); + } catch (JsonParseException e) { + source.sendMessage(Component.text() + .content("An error occurred on the Velocity-servers and the dump could not " + + "be completed. Please contact the Velocity staff about this problem. " + + "If you do, provide the details about this error from the Velocity " + + "console or server log.") + .color(NamedTextColor.RED).build()); + logger.error("Invalid response from the Velocity servers: " + e.getMessage()); + e.printStackTrace(); } - } catch (JsonSyntaxException e4) { - // Mangled json - source.sendMessage( - Component.text() - .content("Server responded with invalid data!") - .color(NamedTextColor.RED).build()); - return; - } catch (JsonParseException e5) { - // Backend error? - source.sendMessage( - Component.text() - .content("Data was uploaded successfully but couldn't be posted") - .color(NamedTextColor.RED).build()); - return; - } - TextComponent response = Component.text() - .content("Created an anonymised report containing useful information about") - .append(Component.newline() - .append( - Component.text("this proxy. If a developer requested it" - + ", you may share the")) - .append(Component.newline()) - .append(Component.text("following link with them:")) - .append(Component.newline()) - .append(Component.text("https://dump.velocitypowered.com/" - + returned.get("key").getAsString() + ".json") - .color(NamedTextColor.GREEN))) - .append(Component.newline()) - .append(Component.text("Note: This link is only valid for a few days")) - .build(); - source.sendMessage(response); - + }, MoreExecutors.directExecutor()); } @Override 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 9016ae3ff..c75597f4d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java @@ -173,8 +173,6 @@ public enum InformationUtils { SocketAddress address = server.getServerInfo().getAddress(); if (address instanceof InetSocketAddress) { InetSocketAddress iaddr = (InetSocketAddress) address; - info.addProperty("socketType", "EventLoop/NIO"); - info.addProperty("unresolved", iaddr.isUnresolved()); if (iaddr.isUnresolved()) { // Greetings form Netty 4aa10db9 info.addProperty("host", iaddr.getHostString()); @@ -184,10 +182,8 @@ public enum InformationUtils { info.addProperty("port", iaddr.getPort()); } else if (address instanceof DomainSocketAddress) { DomainSocketAddress daddr = (DomainSocketAddress) address; - info.addProperty("socketType", "Unix/Epoll"); info.addProperty("path", daddr.path()); } else { - info.addProperty("socketType", "Unknown/Generic"); info.addProperty("info", address.toString()); } return info; From 536049995d30f7c8fb0f70f689a86bc8d22470d3 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 26 Oct 2020 20:58:20 -0400 Subject: [PATCH 37/39] Fix SpotBugs complaint --- .../proxy/util/InformationUtils.java | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) 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 c75597f4d..f53e3b5c8 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java @@ -16,13 +16,10 @@ import com.velocitypowered.api.proxy.config.ProxyConfig; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.ProxyVersion; -import io.netty.channel.unix.DomainSocketAddress; - import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.SocketAddress; import java.util.List; import java.util.Map; @@ -170,22 +167,14 @@ public enum InformationUtils { public static JsonObject collectServerInfo(RegisteredServer server) { JsonObject info = new JsonObject(); info.addProperty("currentPlayers", server.getPlayersConnected().size()); - SocketAddress address = server.getServerInfo().getAddress(); - if (address instanceof InetSocketAddress) { - InetSocketAddress iaddr = (InetSocketAddress) address; - if (iaddr.isUnresolved()) { - // Greetings form Netty 4aa10db9 - info.addProperty("host", iaddr.getHostString()); - } else { - info.addProperty("host", anonymizeInetAddress(iaddr.getAddress())); - } - info.addProperty("port", iaddr.getPort()); - } else if (address instanceof DomainSocketAddress) { - DomainSocketAddress daddr = (DomainSocketAddress) address; - info.addProperty("path", daddr.path()); + InetSocketAddress iaddr = server.getServerInfo().getAddress(); + if (iaddr.isUnresolved()) { + // Greetings form Netty 4aa10db9 + info.addProperty("host", iaddr.getHostString()); } else { - info.addProperty("info", address.toString()); + info.addProperty("host", anonymizeInetAddress(iaddr.getAddress())); } + info.addProperty("port", iaddr.getPort()); return info; } From 9f424522ac64400f24be83a62de023ecce8906e6 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 26 Oct 2020 21:00:08 -0400 Subject: [PATCH 38/39] Authors should be a JSON array --- .../com/velocitypowered/proxy/util/InformationUtils.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 f53e3b5c8..89f24ab9c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java @@ -23,8 +23,6 @@ import java.net.InetSocketAddress; import java.util.List; import java.util.Map; -import joptsimple.internal.Strings; - public enum InformationUtils { ; @@ -51,8 +49,11 @@ public enum InformationUtils { current.addProperty("version", desc.getVersion().get()); } if (!desc.getAuthors().isEmpty()) { - current.addProperty("authors", - Strings.join(desc.getAuthors(), ",")); + JsonArray authorsArray = new JsonArray(); + for (String author : desc.getAuthors()) { + authorsArray.add(author); + } + current.add("authors", authorsArray); } if (desc.getDescription().isPresent()) { current.addProperty("description", desc.getDescription().get()); From 188758cf0e1780e80b2c4d31ea3c04a0d7b79ae3 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 26 Oct 2020 21:05:09 -0400 Subject: [PATCH 39/39] Drop 1.16.4 snapshot support in anticipation for full 1.16.4 release --- .../java/com/velocitypowered/api/network/ProtocolVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 830911c0e..ce5130078 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -41,7 +41,7 @@ public enum ProtocolVersion { MINECRAFT_1_16_1(736, "1.16.1"), MINECRAFT_1_16_2(751, "1.16.2"), MINECRAFT_1_16_3(753, "1.16.3"), - MINECRAFT_1_16_4(754, 2, "1.16.4"); + MINECRAFT_1_16_4(754, "1.16.4"); private static final int SNAPSHOT_BIT = 30;