Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-16 21:10:30 +01:00
Add arbitrary chat tab complete event. Fixes #236
Dieser Commit ist enthalten in:
Ursprung
19cec571d0
Commit
b4e62443c9
@ -0,0 +1,52 @@
|
||||
package com.velocitypowered.api.event.player;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This event is fired after a tab complete response is sent by the remote server, for clients on
|
||||
* 1.12.2 and below. You have the opportunity to modify the response sent to the remote player.
|
||||
*/
|
||||
public class TabCompleteEvent {
|
||||
private final Player player;
|
||||
private final String partialMessage;
|
||||
private final List<String> suggestions;
|
||||
|
||||
public TabCompleteEvent(Player player, String partialMessage, List<String> suggestions) {
|
||||
this.player = checkNotNull(player, "player");
|
||||
this.partialMessage = checkNotNull(partialMessage, "partialMessage");
|
||||
this.suggestions = new ArrayList<>(checkNotNull(suggestions, "suggestions"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the player requesting the tab completion.
|
||||
* @return the requesting player
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message being partially completed.
|
||||
* @return
|
||||
*/
|
||||
public String getPartialMessage() {
|
||||
return partialMessage;
|
||||
}
|
||||
|
||||
public List<String> getSuggestions() {
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TabCompleteEvent{"
|
||||
+ "player=" + player
|
||||
+ ", partialMessage='" + partialMessage + '\''
|
||||
+ ", suggestions=" + suggestions
|
||||
+ '}';
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.construc
|
||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||
import com.velocitypowered.api.event.player.PlayerChatEvent;
|
||||
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
|
||||
import com.velocitypowered.api.event.player.TabCompleteEvent;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
@ -35,12 +36,11 @@ import io.netty.util.ReferenceCountUtil;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.TextComponent;
|
||||
import net.kyori.text.format.TextColor;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@ -60,7 +60,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
private final List<UUID> serverBossBars = new ArrayList<>();
|
||||
private final Queue<PluginMessage> loginPluginMessages = new ArrayDeque<>();
|
||||
private final VelocityServer server;
|
||||
private @Nullable TabCompleteRequest legacyCommandTabComplete;
|
||||
private @Nullable TabCompleteRequest outstandingTabComplete;
|
||||
|
||||
/**
|
||||
* Constructs a client play session handler.
|
||||
@ -156,25 +156,27 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
public boolean handle(TabCompleteRequest packet) {
|
||||
boolean isCommand = !packet.isAssumeCommand() && packet.getCommand().startsWith("/");
|
||||
|
||||
if (!isCommand) {
|
||||
// We can't deal with anything else.
|
||||
return false;
|
||||
if (isCommand) {
|
||||
return this.handleTabCompleteForCommand(packet);
|
||||
} else {
|
||||
return this.handleRegularTabComplete(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleTabCompleteForCommand(TabCompleteRequest packet) {
|
||||
// In 1.13+, we need to do additional work for the richer suggestions available.
|
||||
String command = packet.getCommand().substring(1);
|
||||
int spacePos = command.indexOf(' ');
|
||||
if (spacePos == -1) {
|
||||
return false;
|
||||
spacePos = command.length();
|
||||
}
|
||||
|
||||
String commandLabel = command.substring(0, spacePos);
|
||||
if (!server.getCommandManager().hasCommand(commandLabel)) {
|
||||
if (player.getProtocolVersion().compareTo(MINECRAFT_1_13) < 0) {
|
||||
// Outstanding tab completes are recorded for use with 1.12 clients and below to provide
|
||||
// tab list completion support for command names. In 1.13, Brigadier handles everything for
|
||||
// us.
|
||||
legacyCommandTabComplete = packet;
|
||||
// additional tab completion support.
|
||||
outstandingTabComplete = packet;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -213,6 +215,15 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleRegularTabComplete(TabCompleteRequest packet) {
|
||||
if (player.getProtocolVersion().compareTo(MINECRAFT_1_13) < 0) {
|
||||
// Outstanding tab completes are recorded for use with 1.12 clients and below to provide
|
||||
// additional tab completion support.
|
||||
outstandingTabComplete = packet;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(PluginMessage packet) {
|
||||
VelocityServerConnection serverConn = player.getConnectedServer();
|
||||
@ -412,28 +423,53 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles additional tab complete for 1.12 and lower clients.
|
||||
* Handles additional tab complete.
|
||||
*
|
||||
* @param response the tab complete response from the backend
|
||||
*/
|
||||
public void handleTabCompleteResponse(TabCompleteResponse response) {
|
||||
if (legacyCommandTabComplete != null) {
|
||||
String command = legacyCommandTabComplete.getCommand().substring(1);
|
||||
try {
|
||||
List<String> offers = server.getCommandManager().offerSuggestions(player, command);
|
||||
for (String offer : offers) {
|
||||
response.getOffers().add(new Offer(offer, null));
|
||||
}
|
||||
response.getOffers().sort(null);
|
||||
} catch (Exception e) {
|
||||
logger.error("Unable to provide tab list completions for {} for command '{}'",
|
||||
player.getUsername(),
|
||||
command, e);
|
||||
if (outstandingTabComplete != null) {
|
||||
if (outstandingTabComplete.isAssumeCommand()) {
|
||||
return; // used for command blocks which can't run Velocity commands anyway
|
||||
}
|
||||
legacyCommandTabComplete = null;
|
||||
if (outstandingTabComplete.getCommand().startsWith("/")) {
|
||||
this.finishCommandTabComplete(outstandingTabComplete, response);
|
||||
} else {
|
||||
this.finishRegularTabComplete(outstandingTabComplete, response);
|
||||
}
|
||||
outstandingTabComplete = null;
|
||||
}
|
||||
}
|
||||
|
||||
player.getMinecraftConnection().write(response);
|
||||
private void finishCommandTabComplete(TabCompleteRequest request, TabCompleteResponse response) {
|
||||
String command = request.getCommand().substring(1);
|
||||
try {
|
||||
List<String> offers = server.getCommandManager().offerSuggestions(player, command);
|
||||
for (String offer : offers) {
|
||||
response.getOffers().add(new Offer(offer, null));
|
||||
}
|
||||
response.getOffers().sort(null);
|
||||
player.getMinecraftConnection().write(response);
|
||||
} catch (Exception e) {
|
||||
logger.error("Unable to provide tab list completions for {} for command '{}'",
|
||||
player.getUsername(),
|
||||
command, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void finishRegularTabComplete(TabCompleteRequest request, TabCompleteResponse response) {
|
||||
List<String> offers = new ArrayList<>();
|
||||
for (Offer offer : response.getOffers()) {
|
||||
offers.add(offer.getText());
|
||||
}
|
||||
server.getEventManager().fire(new TabCompleteEvent(player, request.getCommand(), offers))
|
||||
.thenAcceptAsync(e -> {
|
||||
response.getOffers().clear();
|
||||
for (String s : e.getSuggestions()) {
|
||||
response.getOffers().add(new Offer(s));
|
||||
}
|
||||
player.getMinecraftConnection().write(response);
|
||||
}, player.getMinecraftConnection().eventLoop());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,5 +133,9 @@ public class TabCompleteResponse implements MinecraftPacket {
|
||||
public int compareTo(Offer o) {
|
||||
return this.text.compareTo(o.text);
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren