diff --git a/api/src/main/java/net/md_5/bungee/api/event/BrigadierSuggestionsEvent.java b/api/src/main/java/net/md_5/bungee/api/event/BrigadierSuggestionsEvent.java new file mode 100644 index 00000000..81c1ad36 --- /dev/null +++ b/api/src/main/java/net/md_5/bungee/api/event/BrigadierSuggestionsEvent.java @@ -0,0 +1,40 @@ +package net.md_5.bungee.api.event; + +import com.mojang.brigadier.suggestion.Suggestions; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import net.md_5.bungee.api.connection.Connection; +import net.md_5.bungee.api.plugin.Cancellable; + +/** + * Event called when an 1.13+ player uses tab completion. + * This event is fired after {@link TabCompleteEvent}. + */ +@Data +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class BrigadierSuggestionsEvent extends TargetedEvent implements Cancellable +{ + + /** + * Cancelled state. + */ + private boolean cancelled; + /** + * The message the player has already entered. + */ + private final String cursor; + /** + * The suggestions that will be sent to the client. If this list is empty, + * the request will be forwarded to the server. + */ + private Suggestions suggestions; + + public BrigadierSuggestionsEvent(Connection sender, Connection receiver, String cursor, Suggestions suggestions) + { + super( sender, receiver ); + this.cursor = cursor; + this.suggestions = suggestions; + } +} diff --git a/api/src/main/java/net/md_5/bungee/api/event/TabCompleteEvent.java b/api/src/main/java/net/md_5/bungee/api/event/TabCompleteEvent.java index 2fcd8616..d8055c36 100644 --- a/api/src/main/java/net/md_5/bungee/api/event/TabCompleteEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/event/TabCompleteEvent.java @@ -9,6 +9,8 @@ import net.md_5.bungee.api.plugin.Cancellable; /** * Event called when a player uses tab completion. + * For 1.13+ clients, you can also use {@link BrigadierSuggestionsEvent} for + * more control of the suggestions. */ @Data @ToString(callSuper = true) diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java index f150d8fa..7f8276a8 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java @@ -6,7 +6,6 @@ import com.mojang.brigadier.suggestion.Suggestion; import com.mojang.brigadier.suggestion.Suggestions; import io.netty.channel.Channel; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import net.md_5.bungee.BungeeCord; import net.md_5.bungee.ServerConnection.KeepAliveData; @@ -14,6 +13,7 @@ import net.md_5.bungee.UserConnection; import net.md_5.bungee.Util; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.BrigadierSuggestionsEvent; import net.md_5.bungee.api.event.ChatEvent; import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.PluginMessageEvent; @@ -172,34 +172,48 @@ public class UpstreamBridge extends PacketHandler TabCompleteEvent tabCompleteEvent = new TabCompleteEvent( con, con.getServer(), tabComplete.getCursor(), suggestions ); bungee.getPluginManager().callEvent( tabCompleteEvent ); - if ( tabCompleteEvent.isCancelled() ) + List legacyResults = tabCompleteEvent.getSuggestions(); + + if ( con.getPendingConnection().getVersion() < ProtocolConstants.MINECRAFT_1_13 ) { - throw CancelSendSignal.INSTANCE; - } - - List results = tabCompleteEvent.getSuggestions(); - if ( !results.isEmpty() ) - { - // Unclear how to handle 1.13 commands at this point. Because we don't inject into the command packets we are unlikely to get this far unless - // Bungee plugins are adding results for commands they don't own anyway - if ( con.getPendingConnection().getVersion() < ProtocolConstants.MINECRAFT_1_13 ) + if ( tabCompleteEvent.isCancelled() ) { - con.unsafe().sendPacket( new TabCompleteResponse( results ) ); - } else - { - int start = tabComplete.getCursor().lastIndexOf( ' ' ) + 1; - int end = tabComplete.getCursor().length(); - StringRange range = StringRange.between( start, end ); - - List brigadier = new LinkedList<>(); - for ( String s : results ) - { - brigadier.add( new Suggestion( range, s ) ); - } - - con.unsafe().sendPacket( new TabCompleteResponse( tabComplete.getTransactionId(), new Suggestions( range, brigadier ) ) ); + throw CancelSendSignal.INSTANCE; + } + + if ( !legacyResults.isEmpty() ) + { + con.unsafe().sendPacket( new TabCompleteResponse( legacyResults ) ); + throw CancelSendSignal.INSTANCE; + } + } else + { + int start = tabComplete.getCursor().lastIndexOf( ' ' ) + 1; + int end = tabComplete.getCursor().length(); + StringRange lastArgumentRange = StringRange.between( start, end ); + + List brigadier = new ArrayList<>( legacyResults.size() ); + for ( String s : legacyResults ) + { + brigadier.add( new Suggestion( lastArgumentRange, s ) ); + } + + BrigadierSuggestionsEvent tabCompleteRequestEvent = new BrigadierSuggestionsEvent( con, con.getServer(), tabComplete.getCursor(), new Suggestions( lastArgumentRange, brigadier ) ); + tabCompleteRequestEvent.setCancelled( tabCompleteEvent.isCancelled() ); + bungee.getPluginManager().callEvent( tabCompleteRequestEvent ); + + if ( tabCompleteRequestEvent.isCancelled() ) + { + throw CancelSendSignal.INSTANCE; + } + + Suggestions brigadierResults = tabCompleteRequestEvent.getSuggestions(); + + if ( !brigadierResults.isEmpty() ) + { + con.unsafe().sendPacket( new TabCompleteResponse( tabComplete.getTransactionId(), brigadierResults ) ); + throw CancelSendSignal.INSTANCE; } - throw CancelSendSignal.INSTANCE; } }