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 7793bf07..a6ec96c6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -66,6 +66,7 @@ public class VelocityCommandManager implements CommandManager { if (split.length == 1) { return Optional.of(commands.keySet().stream() .filter(cmd -> cmd.regionMatches(true, 0, command, 0, command.length())) + .map(cmd -> "/" + cmd) .collect(Collectors.toList())); } 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 fea65c6a..88cba258 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 @@ -98,6 +98,8 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { } }, connection.getMinecraftConnection().getChannel().eventLoop()); } + } else if (packet instanceof TabCompleteResponse) { + playerHandler.handleTabCompleteResponse((TabCompleteResponse) packet); } else if (connection.hasCompletedJoin()) { // Just forward the packet on. We don't have anything to handle at this time. connection.getPlayer().getConnection().write(packet); 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 91d2d64a..0672839d 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 @@ -36,6 +36,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { private final Set clientPluginMsgChannels = new HashSet<>(); private final Queue loginPluginMessages = new ArrayDeque<>(); private final VelocityServer server; + private TabCompleteRequest outstandingTabComplete; public ClientPlaySessionHandler(VelocityServer server, ConnectedPlayer player) { this.player = player; @@ -100,31 +101,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } if (packet instanceof TabCompleteRequest) { - TabCompleteRequest req = (TabCompleteRequest) packet; - int lastSpace = req.getCommand().indexOf(' '); - if (!req.isAssumeCommand() && lastSpace != -1) { - String command = req.getCommand().substring(1); - try { - Optional> offers = server.getCommandManager().offerSuggestions(player, command); - if (offers.isPresent()) { - TabCompleteResponse response = new TabCompleteResponse(); - response.setTransactionId(req.getTransactionId()); - response.setStart(lastSpace); - response.setLength(req.getCommand().length() - lastSpace); - - for (String s : offers.get()) { - response.getOffers().add(new TabCompleteResponse.Offer(s, null)); - } - - player.getConnection().write(response); - } else { - serverConnection.getMinecraftConnection().write(packet); - } - } catch (Exception e) { - logger.error("Unable to provide tab list completions for " + player.getUsername() + " for command '" + req.getCommand() + "'", e); - } - return; - } + // Record the request so that the outstanding request can be augmented later. + outstandingTabComplete = (TabCompleteRequest) packet; + serverConnection.getMinecraftConnection().write(packet); } if (packet instanceof PluginMessage) { @@ -327,4 +306,21 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { this.lastPingID = -1; this.lastPingSent = -1; } + + public void handleTabCompleteResponse(TabCompleteResponse response) { + if (outstandingTabComplete != null) { + if (!outstandingTabComplete.isAssumeCommand()) { + String command = outstandingTabComplete.getCommand().substring(1); + try { + Optional> offers = server.getCommandManager().offerSuggestions(player, command); + offers.ifPresent(strings -> response.getOffers().addAll(strings)); + } catch (Exception e) { + logger.error("Unable to provide tab list completions for {} for command '{}'", player.getUsername(), + command, e); + } + outstandingTabComplete = null; + } + player.getConnection().write(response); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index 8056c382..8f702ee6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -40,8 +40,7 @@ public enum StateRegistry { map(0x14, MINECRAFT_1_8, false), map(0x01, MINECRAFT_1_9, false), map(0x02, MINECRAFT_1_12, false), - map(0x01, MINECRAFT_1_12_1, false), - map(0x05, MINECRAFT_1_13, false)); + map(0x01, MINECRAFT_1_12_1, false)); SERVERBOUND.register(Chat.class, Chat::new, map(0x01, MINECRAFT_1_8, false), map(0x02, MINECRAFT_1_9, false), @@ -77,10 +76,9 @@ public enum StateRegistry { map(0x0F, MINECRAFT_1_12, true), map(0x0E, MINECRAFT_1_13, true)); CLIENTBOUND.register(TabCompleteResponse.class, TabCompleteResponse::new, - map(0x3A, MINECRAFT_1_8, true), - map(0x0E, MINECRAFT_1_9, true), - map(0x0E, MINECRAFT_1_12, true), - map(0x10, MINECRAFT_1_13, true)); + map(0x3A, MINECRAFT_1_8, false), + map(0x0E, MINECRAFT_1_9, false), + map(0x0E, MINECRAFT_1_12, false)); CLIENTBOUND.register(PluginMessage.class, PluginMessage::new, map(0x3F, MINECRAFT_1_8, false), map(0x18, MINECRAFT_1_9, false), diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java index 10c14b40..a3636f85 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java @@ -9,20 +9,11 @@ import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_1 import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_9; public class TabCompleteRequest implements MinecraftPacket { - private int transactionId; private String command; private boolean assumeCommand; private boolean hasPosition; private long position; - public int getTransactionId() { - return transactionId; - } - - public void setTransactionId(int transactionId) { - this.transactionId = transactionId; - } - public String getCommand() { return command; } @@ -58,8 +49,7 @@ public class TabCompleteRequest implements MinecraftPacket { @Override public String toString() { return "TabCompleteRequest{" + - "transactionId=" + transactionId + - ", command='" + command + '\'' + + "command='" + command + '\'' + ", assumeCommand=" + assumeCommand + ", hasPosition=" + hasPosition + ", position=" + position + @@ -68,35 +58,25 @@ public class TabCompleteRequest implements MinecraftPacket { @Override public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - if (protocolVersion >= MINECRAFT_1_13) { - this.transactionId = ProtocolUtils.readVarInt(buf); - this.command = ProtocolUtils.readString(buf); - } else { - this.command = ProtocolUtils.readString(buf); - if (protocolVersion >= MINECRAFT_1_9) { - this.assumeCommand = buf.readBoolean(); - } - this.hasPosition = buf.readBoolean(); - if (hasPosition) { - this.position = buf.readLong(); - } + this.command = ProtocolUtils.readString(buf); + if (protocolVersion >= MINECRAFT_1_9) { + this.assumeCommand = buf.readBoolean(); + } + this.hasPosition = buf.readBoolean(); + if (hasPosition) { + this.position = buf.readLong(); } } @Override public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - if (protocolVersion >= MINECRAFT_1_13) { - ProtocolUtils.writeVarInt(buf, transactionId); - ProtocolUtils.writeString(buf, command); - } else { - ProtocolUtils.writeString(buf, command); - if (protocolVersion >= MINECRAFT_1_9) { - buf.writeBoolean(assumeCommand); - } - buf.writeBoolean(hasPosition); - if (hasPosition) { - buf.writeLong(position); - } + ProtocolUtils.writeString(buf, command); + if (protocolVersion >= MINECRAFT_1_9) { + buf.writeBoolean(assumeCommand); + } + buf.writeBoolean(hasPosition); + if (hasPosition) { + buf.writeLong(position); } } } 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 f5464360..0a528acd 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 @@ -4,125 +4,37 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -import net.kyori.text.Component; -import net.kyori.text.serializer.ComponentSerializers; -import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; import java.util.List; -import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_13; - public class TabCompleteResponse implements MinecraftPacket { - private int transactionId; - private int start; - private int length; - private final List offers = new ArrayList<>(); + private final List offers = new ArrayList<>(); - public int getTransactionId() { - return transactionId; - } - - public void setTransactionId(int transactionId) { - this.transactionId = transactionId; - } - - public int getStart() { - return start; - } - - public void setStart(int start) { - this.start = start; - } - - public int getLength() { - return length; - } - - public void setLength(int length) { - this.length = length; - } - - public List getOffers() { + public List getOffers() { return offers; } @Override public String toString() { return "TabCompleteResponse{" + - "transactionId=" + transactionId + - ", start=" + start + - ", length=" + length + - ", offers=" + offers + + "offers=" + offers + '}'; } @Override public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - if (protocolVersion >= MINECRAFT_1_13) { - this.transactionId = ProtocolUtils.readVarInt(buf); - this.start = ProtocolUtils.readVarInt(buf); - this.length = ProtocolUtils.readVarInt(buf); - int offersAvailable = ProtocolUtils.readVarInt(buf); - for (int i = 0; i < offersAvailable; i++) { - String entry = ProtocolUtils.readString(buf); - Component component = buf.readBoolean() ? ComponentSerializers.JSON.deserialize(ProtocolUtils.readString(buf)) : - null; - offers.add(new Offer(entry, component)); - } - } else { - int offersAvailable = ProtocolUtils.readVarInt(buf); - for (int i = 0; i < offersAvailable; i++) { - offers.add(new Offer(ProtocolUtils.readString(buf), null)); - } + int offersAvailable = ProtocolUtils.readVarInt(buf); + for (int i = 0; i < offersAvailable; i++) { + offers.add(ProtocolUtils.readString(buf)); } } @Override public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - if (protocolVersion >= MINECRAFT_1_13) { - ProtocolUtils.writeVarInt(buf, transactionId); - ProtocolUtils.writeVarInt(buf, start); - ProtocolUtils.writeVarInt(buf, length); - ProtocolUtils.writeVarInt(buf, offers.size()); - for (Offer offer : offers) { - ProtocolUtils.writeString(buf, offer.entry); - buf.writeBoolean(offer.tooltip != null); - if (offer.tooltip != null) { - ProtocolUtils.writeString(buf, ComponentSerializers.JSON.serialize(offer.tooltip)); - } - } - } else { - ProtocolUtils.writeVarInt(buf, offers.size()); - for (Offer offer : offers) { - ProtocolUtils.writeString(buf, offer.entry); - } - } - } - - public static class Offer { - private final String entry; - private final Component tooltip; - - public Offer(String entry, @Nullable Component tooltip) { - this.entry = entry; - this.tooltip = tooltip; - } - - public String getEntry() { - return entry; - } - - public @Nullable Component getTooltip() { - return tooltip; - } - - @Override - public String toString() { - return "Offer{" + - "entry='" + entry + '\'' + - ", tooltip=" + tooltip + - '}'; + ProtocolUtils.writeVarInt(buf, offers.size()); + for (String offer : offers) { + ProtocolUtils.writeString(buf, offer); } } }