diff --git a/api/build.gradle b/api/build.gradle index 168f7b6b2..69b9a090c 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -8,6 +8,11 @@ apply from: '../gradle/checkstyle.gradle' apply from: '../gradle/publish.gradle' apply plugin: 'com.github.johnrengelman.shadow' +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + sourceSets { ap { compileClasspath += main.compileClasspath + main.output @@ -32,7 +37,7 @@ dependencies { 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' + api 'com.velocitypowered:velocity-brigadier:1.0.0-SNAPSHOT' api "org.spongepowered:configurate-hocon:${configurateVersion}" api "org.spongepowered:configurate-yaml:${configurateVersion}" diff --git a/build.gradle b/build.gradle index 61f79225f..fcfa2ace8 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ allprojects { junitVersion = '5.7.0' slf4jVersion = '1.7.30' log4jVersion = '2.13.3' - nettyVersion = '4.1.53.Final' + nettyVersion = '4.1.54.Final' guavaVersion = '30.0-jre' checkerFrameworkVersion = '3.6.1' configurateVersion = '4.0.0-SNAPSHOT' @@ -55,9 +55,9 @@ allprojects { url 'https://oss.sonatype.org/content/groups/public/' } - // Brigadier + // Velocity repo maven { - url "https://libraries.minecraft.net" + url "https://nexus.velocitypowered.com/repository/maven-public/" } } diff --git a/native/build.gradle b/native/build.gradle index ed7cf0aa3..0a348523f 100644 --- a/native/build.gradle +++ b/native/build.gradle @@ -7,6 +7,11 @@ plugins { apply from: '../gradle/checkstyle.gradle' apply from: '../gradle/publish.gradle' +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + dependencies { implementation "com.google.guava:guava:${guavaVersion}" implementation "io.netty:netty-handler:${nettyVersion}" diff --git a/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java b/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java index e9cd08c11..9a0ff27bd 100644 --- a/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java +++ b/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java @@ -49,9 +49,9 @@ public class JavaVelocityCompressor implements VelocityCompressor { destination.writerIndex(destination.writerIndex() + produced); } - if (inflater.getBytesWritten() != uncompressedSize) { - throw new DataFormatException("Did not write the exact expected number of" - + " uncompressed bytes, expected " + uncompressedSize); + if (!inflater.finished()) { + throw new DataFormatException("Received a deflate stream that was too large, wanted " + + uncompressedSize); } source.readerIndex(origIdx + inflater.getTotalIn()); } finally { diff --git a/proxy/build.gradle b/proxy/build.gradle index e7eb88a84..ff2e1fe0a 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -8,6 +8,11 @@ plugins { apply from: '../gradle/checkstyle.gradle' apply plugin: 'com.github.johnrengelman.shadow' +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + jar { manifest { def buildNumber = System.getenv("BUILD_NUMBER") ?: "unknown" 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 ba87e39f0..87a554a9b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -99,11 +99,7 @@ public class VelocityCommandManager implements CommandManager { @Override public void unregister(final String alias) { Preconditions.checkNotNull(alias, "alias"); - CommandNode node = - dispatcher.getRoot().getChild(alias.toLowerCase(Locale.ENGLISH)); - if (node != null) { - dispatcher.getRoot().getChildren().remove(node); - } + dispatcher.getRoot().removeChildByName(alias.toLowerCase(Locale.ENGLISH)); } /** 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 86ee58888..ba431ee41 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 @@ -228,8 +228,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { backendConn.write(packet.retain()); } else { byte[] copy = ByteBufUtil.getBytes(packet.content()); - PluginMessageEvent event = new PluginMessageEvent(player, serverConn, id, - ByteBufUtil.getBytes(packet.content())); + PluginMessageEvent event = new PluginMessageEvent(player, serverConn, id, copy); server.getEventManager().fire(event).thenAcceptAsync(pme -> { if (pme.getResult().isAllowed()) { ServerboundPluginMessagePacket message = new ServerboundPluginMessagePacket(packet.getChannel(), diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java index faa430e75..fac1768e0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java @@ -40,31 +40,25 @@ class LegacyForgeUtil { */ static List readModList(AbstractPluginMessagePacket message) { Preconditions.checkNotNull(message, "message"); - Preconditions - .checkArgument(message.getChannel().equals(FORGE_LEGACY_HANDSHAKE_CHANNEL), + Preconditions.checkArgument(message.getChannel().equals(FORGE_LEGACY_HANDSHAKE_CHANNEL), "message is not a FML HS plugin message"); - ByteBuf byteBuf = message.content().retainedSlice(); - try { - byte discriminator = byteBuf.readByte(); + ByteBuf contents = message.content().slice(); + byte discriminator = contents.readByte(); + if (discriminator == MOD_LIST_DISCRIMINATOR) { + ImmutableList.Builder mods = ImmutableList.builder(); + int modCount = ProtocolUtils.readVarInt(contents); - if (discriminator == MOD_LIST_DISCRIMINATOR) { - ImmutableList.Builder mods = ImmutableList.builder(); - int modCount = ProtocolUtils.readVarInt(byteBuf); - - for (int index = 0; index < modCount; index++) { - String id = ProtocolUtils.readString(byteBuf); - String version = ProtocolUtils.readString(byteBuf); - mods.add(new ModInfo.Mod(id, version)); - } - - return mods.build(); + for (int index = 0; index < modCount; index++) { + String id = ProtocolUtils.readString(contents); + String version = ProtocolUtils.readString(contents); + mods.add(new ModInfo.Mod(id, version)); } - return ImmutableList.of(); - } finally { - byteBuf.release(); + return mods.build(); } + + return ImmutableList.of(); } /** diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/serialization/brigadier/ArgumentPropertyRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/network/serialization/brigadier/ArgumentPropertyRegistry.java index 484eab616..2d5b9bcae 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/serialization/brigadier/ArgumentPropertyRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/serialization/brigadier/ArgumentPropertyRegistry.java @@ -76,6 +76,10 @@ public class ArgumentPropertyRegistry { if (property.getResult() != null) { property.getSerializer().serialize(property.getResult(), buf); } + } else if (type instanceof ModArgumentProperty) { + ModArgumentProperty property = (ModArgumentProperty) type; + ProtocolUtils.writeString(buf, property.getIdentifier()); + buf.writeBytes(property.getData()); } else { ArgumentPropertySerializer serializer = byClass.get(type.getClass()); String id = classToId.get(type.getClass()); @@ -98,6 +102,9 @@ public class ArgumentPropertyRegistry { GenericArgumentPropertySerializer.create(BoolArgumentType::bool)); register("brigadier:long", LongArgumentType.class, LONG); + // Crossstitch support + register("crossstitch:mod_argument", ModArgumentProperty.class, MOD); + // Minecraft argument types with extra properties empty("minecraft:entity", ByteArgumentPropertySerializer.BYTE); empty("minecraft:score_holder", ByteArgumentPropertySerializer.BYTE); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/serialization/brigadier/ModArgumentProperty.java b/proxy/src/main/java/com/velocitypowered/proxy/network/serialization/brigadier/ModArgumentProperty.java new file mode 100644 index 000000000..f82133260 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/serialization/brigadier/ModArgumentProperty.java @@ -0,0 +1,47 @@ +package com.velocitypowered.proxy.protocol.packet.brigadier; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; + +public class ModArgumentProperty implements ArgumentType { + + private final String identifier; + private final ByteBuf data; + + public ModArgumentProperty(String identifier, ByteBuf data) { + this.identifier = identifier; + this.data = Unpooled.unreleasableBuffer(data.asReadOnly()); + } + + public String getIdentifier() { + return identifier; + } + + public ByteBuf getData() { + return data.slice(); + } + + @Override + public ByteBuf parse(StringReader reader) throws CommandSyntaxException { + throw new UnsupportedOperationException(); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, + SuggestionsBuilder builder) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection getExamples() { + throw new UnsupportedOperationException(); + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/serialization/brigadier/ModArgumentPropertySerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/network/serialization/brigadier/ModArgumentPropertySerializer.java new file mode 100644 index 000000000..6c6eebbd0 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/serialization/brigadier/ModArgumentPropertySerializer.java @@ -0,0 +1,27 @@ +package com.velocitypowered.proxy.protocol.packet.brigadier; + +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.checkerframework.checker.nullness.qual.Nullable; + +class ModArgumentPropertySerializer implements ArgumentPropertySerializer { + static final ModArgumentPropertySerializer MOD = new ModArgumentPropertySerializer(); + + private ModArgumentPropertySerializer() { + + } + + @Override + public @Nullable ModArgumentProperty deserialize(ByteBuf buf) { + String identifier = ProtocolUtils.readString(buf); + byte[] extraData = ProtocolUtils.readByteArray(buf); + return new ModArgumentProperty(identifier, Unpooled.wrappedBuffer(extraData)); + } + + @Override + public void serialize(ModArgumentProperty object, ByteBuf buf) { + // This is special-cased by ArgumentPropertyRegistry + throw new UnsupportedOperationException(); + } +} 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 232e8c254..bd84db592 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java @@ -175,9 +175,12 @@ public class VelocityPluginManager implements PluginManager { public void addToClasspath(Object plugin, Path path) { checkNotNull(plugin, "instance"); checkNotNull(path, "path"); - checkArgument(pluginInstances.containsKey(plugin), "plugin is not loaded"); + Optional optContainer = fromInstance(plugin); + checkArgument(optContainer.isPresent(), "plugin is not loaded"); + Optional optInstance = optContainer.get().getInstance(); + checkArgument(optInstance.isPresent(), "plugin has no instance"); - ClassLoader pluginClassloader = plugin.getClass().getClassLoader(); + ClassLoader pluginClassloader = optInstance.get().getClass().getClassLoader(); if (pluginClassloader instanceof PluginClassLoader) { ((PluginClassLoader) pluginClassloader).addPath(path); } else { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java index 8617e2013..a15c286ab 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -32,7 +32,8 @@ import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoop; import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.Collection; -import java.util.Set; +import java.util.Map; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -45,7 +46,7 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud private final @Nullable VelocityServer server; private final ServerInfo serverInfo; - private final Set players = ConcurrentHashMap.newKeySet(); + private final Map players = new ConcurrentHashMap<>(); public VelocityRegisteredServer(@Nullable VelocityServer server, ServerInfo serverInfo) { this.server = server; @@ -59,7 +60,7 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud @Override public Collection getPlayersConnected() { - return ImmutableList.copyOf(players); + return ImmutableList.copyOf(players.values()); } @Override @@ -111,11 +112,11 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud } public void addPlayer(ConnectedPlayer player) { - players.add(player); + players.put(player.getUniqueId(), player); } public void removePlayer(ConnectedPlayer player) { - players.remove(player); + players.remove(player.getUniqueId(), player); } @Override @@ -130,7 +131,7 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud * @return whether or not the message was sent */ public boolean sendPluginMessage(ChannelIdentifier identifier, ByteBuf data) { - for (ConnectedPlayer player : players) { + for (ConnectedPlayer player : players.values()) { VelocityServerConnection connection = player.getConnectedServer(); if (connection != null && connection.getServerInfo().equals(serverInfo)) { return connection.sendPluginMessage(identifier, data); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java index 8889d76ed..6cf7ecc19 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java @@ -10,6 +10,8 @@ import java.net.SocketAddress; import java.net.URI; public final class AddressUtil { + private static final int DEFAULT_MINECRAFT_PORT = 25565; + private AddressUtil() { throw new AssertionError(); } @@ -26,11 +28,16 @@ public final class AddressUtil { return new DomainSocketAddress(ip.substring("unix://".length())); } URI uri = URI.create("tcp://" + ip); + if (uri.getHost() == null) { + throw new IllegalStateException("Invalid hostname/IP " + ip); + } + + int port = uri.getPort() == -1 ? DEFAULT_MINECRAFT_PORT : uri.getPort(); try { InetAddress ia = InetAddresses.forUriString(uri.getHost()); - return new InetSocketAddress(ia, uri.getPort()); + return new InetSocketAddress(ia, port); } catch (IllegalArgumentException e) { - return InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort()); + return InetSocketAddress.createUnresolved(uri.getHost(), port); } } @@ -47,6 +54,11 @@ public final class AddressUtil { } Preconditions.checkNotNull(ip, "ip"); URI uri = URI.create("tcp://" + ip); - return new InetSocketAddress(uri.getHost(), uri.getPort()); + if (uri.getHost() == null) { + throw new IllegalStateException("Invalid hostname/IP " + ip); + } + + int port = uri.getPort() == -1 ? DEFAULT_MINECRAFT_PORT : uri.getPort(); + return new InetSocketAddress(uri.getHost(), port); } } diff --git a/proxy/src/test/java/com/velocitypowered/proxy/command/CommandManagerTests.java b/proxy/src/test/java/com/velocitypowered/proxy/command/CommandManagerTests.java index 81384b666..34d3f8336 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/command/CommandManagerTests.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/command/CommandManagerTests.java @@ -92,6 +92,7 @@ public class CommandManagerTests { assertTrue(manager.hasCommand("foO")); manager.unregister("fOo"); assertFalse(manager.hasCommand("foo")); + assertFalse(manager.execute(MockCommandSource.INSTANCE, "foo").join()); manager.register("foo", command, "bAr", "BAZ"); assertTrue(manager.hasCommand("bar"));