diff --git a/api/build.gradle b/api/build.gradle index 91b9d2bd4..4a275b55f 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -1,12 +1,10 @@ plugins { - id 'java' + id 'java-library' id 'maven-publish' id 'checkstyle' - id "net.ltgt.errorprone" version "0.8" } apply from: '../gradle/checkstyle.gradle' -apply from: '../gradle/errorprone.gradle' apply plugin: 'com.github.johnrengelman.shadow' sourceSets { @@ -16,35 +14,35 @@ sourceSets { } dependencies { - compile 'com.google.code.gson:gson:2.8.6' - compile "com.google.guava:guava:${guavaVersion}" + api 'com.google.code.gson:gson:2.8.6' + api "com.google.guava:guava:${guavaVersion}" // DEPRECATED: Will be removed in Velocity 2.0.0 - compile "net.kyori:text-api:${textVersion}" - compile "net.kyori:text-serializer-gson:${textVersion}" - compile "net.kyori:text-serializer-legacy:${textVersion}" - compile "net.kyori:text-serializer-plain:${textVersion}" + api "net.kyori:text-api:${textVersion}" + api "net.kyori:text-serializer-gson:${textVersion}" + api "net.kyori:text-serializer-legacy:${textVersion}" + api "net.kyori:text-serializer-plain:${textVersion}" // DEPRECATED: Will be removed in Velocity 2.0.0 - compile 'com.moandjiezana.toml:toml4j:0.7.2' + api 'com.moandjiezana.toml:toml4j:0.7.2' - compile "net.kyori:adventure-api:${adventureVersion}" - compile "net.kyori:adventure-text-serializer-gson:${adventureVersion}" - compile "net.kyori:adventure-text-serializer-legacy:${adventureVersion}" - compile "net.kyori:adventure-text-serializer-plain:${adventureVersion}" - compile "net.kyori:adventure-text-serializer-legacy-text3:${adventureVersion}" + api "net.kyori:adventure-api:${adventureVersion}" + 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}" - compile "org.slf4j:slf4j-api:${slf4jVersion}" - compile 'com.google.inject:guice:4.2.3' - compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}" - compile 'com.mojang:brigadier:1.0.17' + api "org.slf4j:slf4j-api:${slf4jVersion}" + api 'com.google.inject:guice:4.2.3' + api "org.checkerframework:checker-qual:${checkerFrameworkVersion}" + api 'com.mojang:brigadier:1.0.17' - compile "org.spongepowered:configurate-hocon:${configurateVersion}" - compile "org.spongepowered:configurate-yaml:${configurateVersion}" - compile "org.spongepowered:configurate-gson:${configurateVersion}" + api "org.spongepowered:configurate-hocon:${configurateVersion}" + api "org.spongepowered:configurate-yaml:${configurateVersion}" + api "org.spongepowered:configurate-gson:${configurateVersion}" - testCompile "org.junit.jupiter:junit-jupiter-api:${junitVersion}" - testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" + testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}" + testImplementation "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" } task javadocJar(type: Jar) { diff --git a/build.gradle b/build.gradle index fad0011b1..4f6e24165 100644 --- a/build.gradle +++ b/build.gradle @@ -9,9 +9,12 @@ buildscript { plugins { id 'java' + id "com.github.spotbugs" version "4.2.4" apply false } allprojects { + apply plugin: "com.github.spotbugs" + group 'com.velocitypowered' version '1.1.0-SNAPSHOT' @@ -70,4 +73,14 @@ allprojects { junitXml.enabled = true } } + + tasks.withType(com.github.spotbugs.snom.SpotBugsTask) { + reports { + html { + enabled = true + destination = file("$buildDir/reports/spotbugs/main/spotbugs.html") + stylesheet = 'fancy-hist.xsl' + } + } + } } diff --git a/gradle/errorprone.gradle b/gradle/errorprone.gradle deleted file mode 100644 index 4a1334c1f..000000000 --- a/gradle/errorprone.gradle +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - errorprone("com.google.errorprone:error_prone_core:2.3.3") - errorproneJavac("com.google.errorprone:javac:9+181-r4173-1") -} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 54c60fef7..01be9d5c3 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-5.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-all.zip diff --git a/native/build.gradle b/native/build.gradle index 107b5f246..72ed54609 100644 --- a/native/build.gradle +++ b/native/build.gradle @@ -1,17 +1,15 @@ plugins { - id 'java' + id 'java-library' id 'checkstyle' - id "net.ltgt.errorprone" version "0.8" } apply from: '../gradle/checkstyle.gradle' -apply from: '../gradle/errorprone.gradle' dependencies { - compile "com.google.guava:guava:${guavaVersion}" - compile "io.netty:netty-handler:${nettyVersion}" - compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}" + implementation "com.google.guava:guava:${guavaVersion}" + implementation "io.netty:netty-handler:${nettyVersion}" + implementation "org.checkerframework:checker-qual:${checkerFrameworkVersion}" - testCompile "org.junit.jupiter:junit-jupiter-api:${junitVersion}" - testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" + testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}" + testImplementation "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" } \ No newline at end of file diff --git a/proxy/build.gradle b/proxy/build.gradle index 1bfa281e6..88175336f 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -3,11 +3,9 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCach plugins { id 'java' id 'checkstyle' - id "net.ltgt.errorprone" version "0.8" } apply from: '../gradle/checkstyle.gradle' -apply from: '../gradle/errorprone.gradle' apply plugin: 'com.github.johnrengelman.shadow' jar { @@ -38,43 +36,43 @@ tasks.withType(Checkstyle) { dependencies { // Note: we depend on the API twice, first the main sourceset, and then the annotation processor. - compile project(':velocity-api') - compile project(':velocity-api').sourceSets.ap.output - compile project(':velocity-native') + implementation project(':velocity-api') + implementation project(':velocity-api').sourceSets.ap.output + implementation project(':velocity-native') - compile "io.netty:netty-codec:${nettyVersion}" - compile "io.netty:netty-codec-haproxy:${nettyVersion}" - compile "io.netty:netty-codec-http:${nettyVersion}" - compile "io.netty:netty-handler:${nettyVersion}" - compile "io.netty:netty-transport-native-epoll:${nettyVersion}" - compile "io.netty:netty-transport-native-epoll:${nettyVersion}:linux-x86_64" - compile "io.netty:netty-transport-native-epoll:${nettyVersion}:linux-aarch64" - compile "io.netty:netty-resolver-dns:${nettyVersion}" + implementation "io.netty:netty-codec:${nettyVersion}" + implementation "io.netty:netty-codec-haproxy:${nettyVersion}" + implementation "io.netty:netty-codec-http:${nettyVersion}" + implementation "io.netty:netty-handler:${nettyVersion}" + implementation "io.netty:netty-transport-native-epoll:${nettyVersion}" + implementation "io.netty:netty-transport-native-epoll:${nettyVersion}:linux-x86_64" + implementation "io.netty:netty-transport-native-epoll:${nettyVersion}:linux-aarch64" + implementation "io.netty:netty-resolver-dns:${nettyVersion}" - compile "org.apache.logging.log4j:log4j-api:${log4jVersion}" - compile "org.apache.logging.log4j:log4j-core:${log4jVersion}" - compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4jVersion}" - compile "org.apache.logging.log4j:log4j-iostreams:${log4jVersion}" + implementation "org.apache.logging.log4j:log4j-api:${log4jVersion}" + implementation "org.apache.logging.log4j:log4j-core:${log4jVersion}" + implementation "org.apache.logging.log4j:log4j-slf4j-impl:${log4jVersion}" + implementation "org.apache.logging.log4j:log4j-iostreams:${log4jVersion}" - compile 'net.sf.jopt-simple:jopt-simple:5.0.4' // command-line options - compile 'net.minecrell:terminalconsoleappender:1.2.0' - runtime 'org.jline:jline-terminal-jansi:3.12.1' // Needed for JLine - runtime 'com.lmax:disruptor:3.4.2' // Async loggers + 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 'com.lmax:disruptor:3.4.2' // Async loggers - compile 'it.unimi.dsi:fastutil:8.2.3' - compile 'net.kyori:event-method-asm:4.0.0-SNAPSHOT' - compile 'net.kyori:adventure-nbt:4.0.0-SNAPSHOT' + implementation 'it.unimi.dsi:fastutil:8.2.3' + implementation 'net.kyori:event-method-asm:4.0.0-SNAPSHOT' + implementation 'net.kyori:adventure-nbt:4.0.0-SNAPSHOT' - compile 'com.mojang:brigadier:1.0.17' + implementation 'org.asynchttpclient:async-http-client:2.10.4' - compile 'org.asynchttpclient:async-http-client:2.10.4' + implementation 'com.spotify:completable-futures:0.3.2' - compile 'com.spotify:completable-futures:0.3.2' + implementation 'com.electronwill.night-config:toml:3.6.3' - compile 'com.electronwill.night-config:toml:3.6.3' + compileOnly 'com.github.spotbugs:spotbugs-annotations:4.1.2' - testCompile "org.junit.jupiter:junit-jupiter-api:${junitVersion}" - testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" + testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}" + testImplementation "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" } shadowJar { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java index b436740da..16ce8cb15 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java @@ -39,12 +39,12 @@ public class Metrics { // The url to which the data is sent private static final String URL = "https://bstats.org/submitData/server-implementation"; - // Should failed requests be logged? - private static boolean logFailedRequests = false; - // The logger for the failed requests private static final Logger logger = LogManager.getLogger(Metrics.class); + // Should failed requests be logged? + private boolean logFailedRequests = false; + // The name of the server software private final String name; @@ -72,7 +72,7 @@ public class Metrics { this.name = name; this.pluginId = pluginId; this.serverUuid = serverUuid; - Metrics.logFailedRequests = logFailedRequests; + this.logFailedRequests = logFailedRequests; this.server = server; // Start submitting the data @@ -262,9 +262,6 @@ public class Metrics { } chart.add("data", data); } catch (Throwable t) { - if (logFailedRequests) { - logger.warn("Failed to get data for custom chart with id {}", chartId, t); - } return null; } return chart; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 88a5d213d..7bbb35f0c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -45,6 +45,7 @@ import com.velocitypowered.proxy.util.bossbar.AdventureBossBarManager; import com.velocitypowered.proxy.util.bossbar.VelocityBossBar; import com.velocitypowered.proxy.util.ratelimit.Ratelimiter; import com.velocitypowered.proxy.util.ratelimit.Ratelimiters; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; @@ -54,7 +55,9 @@ import java.net.InetSocketAddress; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.AccessController; import java.security.KeyPair; +import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Collection; import java.util.Locale; @@ -75,7 +78,6 @@ import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TranslatableComponent; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.text.Component; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -198,21 +200,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { commandManager.register("shutdown", new ShutdownCommand(this),"end"); new GlistCommand(this).register(); - try { - Path configPath = Paths.get("velocity.toml"); - configuration = VelocityConfiguration.read(configPath); - - if (!configuration.validate()) { - logger.error("Your configuration is invalid. Velocity will not start up until the errors " - + "are resolved."); - LogManager.shutdown(); - System.exit(1); - } - } catch (Exception e) { - logger.error("Unable to read/load/save your velocity.toml. The server will shut down.", e); - LogManager.shutdown(); - System.exit(1); - } + this.doStartupConfigLoad(); for (Map.Entry entry : configuration.getServers().entrySet()) { servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue()))); @@ -243,6 +231,25 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { Metrics.VelocityMetrics.startMetrics(this, configuration.getMetrics()); } + @SuppressFBWarnings("DM_EXIT") + private void doStartupConfigLoad() { + try { + Path configPath = Paths.get("velocity.toml"); + configuration = VelocityConfiguration.read(configPath); + + if (!configuration.validate()) { + logger.error("Your configuration is invalid. Velocity will not start up until the errors " + + "are resolved."); + LogManager.shutdown(); + System.exit(1); + } + } catch (Exception e) { + logger.error("Unable to read/load/save your velocity.toml. The server will shut down.", e); + LogManager.shutdown(); + System.exit(1); + } + } + private void loadPlugins() { logger.info("Loading plugins..."); @@ -438,7 +445,14 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { shutdown = true; if (explicitExit) { - System.exit(0); + AccessController.doPrivileged(new PrivilegedAction() { + @Override + @SuppressFBWarnings("DM_EXIT") + public Void run() { + System.exit(0); + return null; + } + }); } }; 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 5e8a5bc15..4d678f406 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -276,7 +276,7 @@ public class VelocityConfiguration implements ProxyConfig { } public byte[] getForwardingSecret() { - return forwardingSecret; + return forwardingSecret.clone(); } @Override 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 cb7eb6657..a14d938a6 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 @@ -13,6 +13,7 @@ import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.util.ByteBufDataInput; import com.velocitypowered.proxy.protocol.util.ByteBufDataOutput; import com.velocitypowered.proxy.server.VelocityRegisteredServer; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import java.util.Optional; @@ -23,6 +24,9 @@ import net.kyori.adventure.text.serializer.ComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +@SuppressFBWarnings(value = "OS_OPEN_STREAM", justification = "Most methods in this class open " + + "instances of ByteBufDataOutput backed by heap-allocated ByteBufs. Closing them does " + + "nothing.") class BungeeCordMessageResponder { private static final MinecraftChannelIdentifier MODERN_CHANNEL = MinecraftChannelIdentifier 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 14258bf4c..9ec3ade7d 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 @@ -305,7 +305,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { TitlePacket subtitlePkt = new TitlePacket(); subtitlePkt.setAction(TitlePacket.SET_SUBTITLE); subtitlePkt.setComponent(serializer.serialize(title.subtitle())); - connection.delayedWrite(titlePkt); + connection.delayedWrite(subtitlePkt); TitlePacket timesPkt = TitlePacket.timesForProtocolVersion(this.getProtocolVersion()); net.kyori.adventure.title.Title.Times times = title.times(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java index cab0da94d..5dc381f2f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java @@ -1,6 +1,7 @@ package com.velocitypowered.proxy.connection.forge.legacy; import com.velocitypowered.proxy.connection.ConnectionTypes; +import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.backend.BackendConnectionPhase; import com.velocitypowered.proxy.connection.backend.BackendConnectionPhases; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; @@ -38,8 +39,9 @@ public enum LegacyForgeHandshakeBackendPhase implements BackendConnectionPhase { void onTransitionToNewPhase(VelocityServerConnection connection) { // We must always reset the handshake before a modded connection is established if // we haven't done so already. - if (connection.getConnection() != null) { - connection.getConnection().setType(ConnectionTypes.LEGACY_FORGE); + MinecraftConnection mc = connection.getConnection(); + if (mc != null) { + mc.setType(ConnectionTypes.LEGACY_FORGE); } connection.getPlayer().sendLegacyForgeHandshakeResetPacket(); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java index b394cb137..da4096cad 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java @@ -2,6 +2,7 @@ package com.velocitypowered.proxy.connection.forge.legacy; import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.proxy.connection.MinecraftConnection; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.client.ClientConnectionPhase; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; @@ -153,8 +154,9 @@ public enum LegacyForgeHandshakeClientPhase implements ClientConnectionPhase { // just in case the timing is awful player.sendKeepAlive(); - if (backendConn.getSessionHandler() instanceof ClientPlaySessionHandler) { - ((ClientPlaySessionHandler) backendConn.getSessionHandler()).flushQueuedMessages(); + MinecraftSessionHandler handler = backendConn.getSessionHandler(); + if (handler instanceof ClientPlaySessionHandler) { + ((ClientPlaySessionHandler) handler).flushQueuedMessages(); } 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 87b0097b6..8d9b40bbf 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java @@ -12,6 +12,8 @@ import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.plugin.PluginManager; import java.lang.reflect.Method; import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Collection; import java.util.IdentityHashMap; @@ -51,7 +53,8 @@ public class VelocityEventManager implements EventManager { public VelocityEventManager(PluginManager pluginManager) { // Expose the event executors to the plugins - required in order for the generated ASM classes // to work. - PluginClassLoader cl = new PluginClassLoader(new URL[0]); + PluginClassLoader cl = AccessController.doPrivileged( + (PrivilegedAction) () -> new PluginClassLoader(new URL[0])); cl.addToClassloaders(); // Initialize the event bus. diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java index 13cb8d0f6..f7de9970e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java @@ -17,10 +17,13 @@ import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription; import java.io.BufferedInputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -58,9 +61,10 @@ public class JavaPluginLoader implements PluginLoader { if (!(source instanceof JavaVelocityPluginDescriptionCandidate)) { throw new IllegalArgumentException("Description provided isn't of the Java plugin loader"); } - PluginClassLoader loader = new PluginClassLoader( - new URL[]{source.getSource().get().toUri().toURL()} - ); + + URL pluginJarUrl = source.getSource().get().toUri().toURL(); + PluginClassLoader loader = AccessController.doPrivileged( + (PrivilegedAction) () -> new PluginClassLoader(new URL[]{pluginJarUrl})); loader.addToClassloaders(); JavaVelocityPluginDescriptionCandidate candidate = 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 2804a978a..bc69fcd39 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -135,7 +135,7 @@ public enum ProtocolUtils { public static void writeString(ByteBuf buf, CharSequence str) { int size = ByteBufUtil.utf8Bytes(str); writeVarInt(buf, size); - ByteBufUtil.writeUtf8(buf, str); + buf.writeCharSequence(str, StandardCharsets.UTF_8); } public static byte[] readByteArray(ByteBuf buf) { @@ -230,12 +230,6 @@ public enum ProtocolUtils { * @return {@link net.kyori.adventure.nbt.CompoundBinaryTag} the CompoundTag from the buffer */ public static CompoundBinaryTag readCompoundTag(ByteBuf buf) { - int indexBefore = buf.readerIndex(); - byte startType = buf.readByte(); - if (startType == 0) { - throw new DecoderException("Invalid NBT start-type (end/empty)"); - } - buf.readerIndex(indexBefore); try { return BinaryTagIO.readDataInput(new ByteBufInputStream(buf)); } catch (IOException thrown) { @@ -250,10 +244,6 @@ public enum ProtocolUtils { * @param compoundTag the CompoundTag to write */ public static void writeCompoundTag(ByteBuf buf, CompoundBinaryTag compoundTag) { - if (compoundTag == null) { - buf.writeByte(0); - return; - } try { BinaryTagIO.writeDataOutput(compoundTag, new ByteBufOutputStream(buf)); } catch (IOException e) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommands.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommands.java index abab244fb..e6ffd5de8 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommands.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommands.java @@ -36,6 +36,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; public class AvailableCommands implements MinecraftPacket { + private static final Command PLACEHOLDER_COMMAND = source -> 0; + private static final byte NODE_TYPE_ROOT = 0x00; private static final byte NODE_TYPE_LITERAL = 0x01; private static final byte NODE_TYPE_ARGUMENT = 0x02; @@ -121,16 +123,14 @@ public class AvailableCommands implements MinecraftPacket { flags |= FLAG_EXECUTABLE; } - if (node instanceof RootCommandNode) { - flags |= NODE_TYPE_ROOT; - } else if (node instanceof LiteralCommandNode) { + if (node instanceof LiteralCommandNode) { flags |= NODE_TYPE_LITERAL; } else if (node instanceof ArgumentCommandNode) { flags |= NODE_TYPE_ARGUMENT; if (((ArgumentCommandNode) node).getCustomSuggestions() != null) { flags |= FLAG_HAS_SUGGESTIONS; } - } else { + } else if (!(node instanceof RootCommandNode)) { throw new IllegalArgumentException("Unknown node type " + node.getClass().getName()); } @@ -238,8 +238,9 @@ public class AvailableCommands implements MinecraftPacket { throw new IllegalStateException("Node points to non-existent index " + redirectTo); } - if (wireNodes[redirectTo].built != null) { - args.redirect(wireNodes[redirectTo].built); + WireNode redirect = wireNodes[redirectTo]; + if (redirect.built != null) { + args.redirect(redirect.built); } else { // Redirect node does not yet exist return false; @@ -248,7 +249,7 @@ public class AvailableCommands implements MinecraftPacket { // If executable, add an empty command if ((flags & FLAG_EXECUTABLE) != 0) { - args.executes((Command) context -> 0); + args.executes(PLACEHOLDER_COMMAND); } this.built = args.build(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequest.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequest.java index a60165b6e..6575ac73f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequest.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequest.java @@ -16,19 +16,19 @@ public class EncryptionRequest implements MinecraftPacket { private byte[] verifyToken = EMPTY_BYTE_ARRAY; public byte[] getPublicKey() { - return publicKey; + return publicKey.clone(); } public void setPublicKey(byte[] publicKey) { - this.publicKey = publicKey; + this.publicKey = publicKey.clone(); } public byte[] getVerifyToken() { - return verifyToken; + return verifyToken.clone(); } public void setVerifyToken(byte[] verifyToken) { - this.verifyToken = verifyToken; + this.verifyToken = verifyToken.clone(); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponse.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponse.java index 04316a8f6..5022f5c18 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponse.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponse.java @@ -15,19 +15,11 @@ public class EncryptionResponse implements MinecraftPacket { private byte[] verifyToken = EMPTY_BYTE_ARRAY; public byte[] getSharedSecret() { - return sharedSecret; - } - - public void setSharedSecret(byte[] sharedSecret) { - this.sharedSecret = sharedSecret; + return sharedSecret.clone(); } public byte[] getVerifyToken() { - return verifyToken; - } - - public void setVerifyToken(byte[] verifyToken) { - this.verifyToken = verifyToken; + return verifyToken.clone(); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java index c21a513e9..c622f9fa0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java @@ -99,7 +99,10 @@ public class PlayerListItem implements MinecraftPacket { ProtocolUtils.writeVarInt(buf, action); ProtocolUtils.writeVarInt(buf, items.size()); for (Item item : items) { - ProtocolUtils.writeUuid(buf, item.getUuid()); + UUID uuid = item.getUuid(); + assert uuid != null : "UUID-less entry serialization attempt - 1.7 component!"; + + ProtocolUtils.writeUuid(buf, uuid); switch (action) { case ADD_PLAYER: ProtocolUtils.writeString(buf, item.getName()); @@ -127,9 +130,10 @@ public class PlayerListItem implements MinecraftPacket { } } else { Item item = items.get(0); - if (item.getDisplayName() != null) { + Component displayNameComponent = item.getDisplayName(); + if (displayNameComponent != null) { String displayName = LegacyComponentSerializer.legacySection() - .serialize(item.getDisplayName()); + .serialize(displayNameComponent); ProtocolUtils.writeString(buf, displayName.length() > 16 ? displayName.substring(0, 16) : displayName); } else { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponse.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponse.java index 3975b3235..859cda0a9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponse.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponse.java @@ -121,6 +121,25 @@ public class TabCompleteResponse implements MinecraftPacket { this.tooltip = tooltip; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Offer offer = (Offer) o; + + return text.equals(offer.text); + } + + @Override + public int hashCode() { + return text.hashCode(); + } + @Override public String toString() { return MoreObjects.toStringHelper(this) 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 cfc101f5f..ff8149332 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 @@ -102,4 +102,8 @@ public class ByteBufDataOutput extends OutputStream implements ByteArrayDataOutp throw new IllegalStateException(e); } } + + @Override + public void close() { + } } 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 6a971a414..f819f9b16 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 @@ -83,8 +83,10 @@ public class VelocityLegacyHoverEventSerializer implements LegacyHoverEventSeria } else { builder.putString("id", keyAsString); } - if (input.nbt() != null) { - builder.put("tag", TagStringIO.get().asCompound(input.nbt().string())); + + BinaryTagHolder nbt = input.nbt(); + if (nbt != null) { + builder.put("tag", TagStringIO.get().asCompound(nbt.string())); } return TextComponent.of(TagStringIO.get().asString(builder.build())); @@ -96,8 +98,9 @@ public class VelocityLegacyHoverEventSerializer implements LegacyHoverEventSeria CompoundBinaryTag.Builder tag = CompoundBinaryTag.builder() .putString("id", input.id().toString()) .putString("type", input.type().asString()); - if (input.name() != null) { - tag.putString("name", componentEncoder.encode(input.name())); + Component name = input.name(); + if (name != null) { + tag.putString("name", componentEncoder.encode(name)); } return TextComponent.of(TagStringIO.get().asString(tag.build())); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java index c60ebe6fa..a8d5e4fa2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java @@ -1,6 +1,9 @@ package com.velocitypowered.proxy.tablist; +import static com.google.common.base.Verify.verify; + import com.google.common.base.Preconditions; +import com.google.common.base.Verify; import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.proxy.player.TabListEntry; import com.velocitypowered.api.util.GameProfile; @@ -121,6 +124,7 @@ public class VelocityTabList implements TabList { // Packets are already forwarded on, so no need to do that here for (PlayerListItem.Item item : packet.getItems()) { UUID uuid = item.getUuid(); + assert uuid != null : "1.7 tab list entry given to modern tab list handler!"; if (packet.getAction() != PlayerListItem.ADD_PLAYER && !entries.containsKey(uuid)) { // Sometimes UPDATE_GAMEMODE is sent before ADD_PLAYER so don't want to warn here diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java index 0b7016fd5..7bb7c0c2e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java @@ -12,6 +12,12 @@ public class VelocityTabListEntryLegacy extends VelocityTabListEntry { super(tabList, profile, displayName, latency, gameMode); } + @Override + public TabListEntry setDisplayName(net.kyori.text.@Nullable Component displayName) { + getTabList().removeEntry(getProfile().getId()); // We have to remove first if updating + return super.setDisplayName(displayName); + } + @Override public TabListEntry setDisplayName(@Nullable Component displayName) { getTabList().removeEntry(getProfile().getId()); // We have to remove first if updating diff --git a/proxy/src/test/java/com/velocitypowered/proxy/command/MockCommandSource.java b/proxy/src/test/java/com/velocitypowered/proxy/command/MockCommandSource.java index 1e946429d..c6dad95f5 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/command/MockCommandSource.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/command/MockCommandSource.java @@ -6,7 +6,7 @@ import net.kyori.text.Component; public class MockCommandSource implements CommandSource { - public static CommandSource INSTANCE = new MockCommandSource(); + public static final CommandSource INSTANCE = new MockCommandSource(); @Override public void sendMessage(final Component component) { diff --git a/proxy/src/test/java/com/velocitypowered/proxy/plugin/MockPluginManager.java b/proxy/src/test/java/com/velocitypowered/proxy/plugin/MockPluginManager.java index 2620fcec5..170c0cdb6 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/plugin/MockPluginManager.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/plugin/MockPluginManager.java @@ -9,7 +9,7 @@ import java.util.Optional; public class MockPluginManager implements PluginManager { - public static PluginManager INSTANCE = new MockPluginManager(); + public static final PluginManager INSTANCE = new MockPluginManager(); @Override public Optional fromInstance(final Object instance) {