Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-06 00:00:47 +01:00
Add MC tab complete (incomplete, only 1.12.2 works)
Dieser Commit ist enthalten in:
Ursprung
bb601dca4b
Commit
54f9de04dc
@ -5,10 +5,7 @@ import com.google.common.collect.ImmutableList;
|
|||||||
import com.velocitypowered.api.command.CommandExecutor;
|
import com.velocitypowered.api.command.CommandExecutor;
|
||||||
import com.velocitypowered.api.command.CommandInvoker;
|
import com.velocitypowered.api.command.CommandInvoker;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class CommandManager {
|
public class CommandManager {
|
||||||
@ -49,30 +46,30 @@ public class CommandManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> offerSuggestions(CommandInvoker invoker, String cmdLine) {
|
public Optional<List<String>> offerSuggestions(CommandInvoker invoker, String cmdLine) {
|
||||||
Preconditions.checkNotNull(invoker, "invoker");
|
Preconditions.checkNotNull(invoker, "invoker");
|
||||||
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||||
|
|
||||||
String[] split = cmdLine.split(" ", -1);
|
String[] split = cmdLine.split(" ", -1);
|
||||||
if (split.length == 0) {
|
if (split.length == 0) {
|
||||||
return ImmutableList.of();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
String command = split[0];
|
String command = split[0];
|
||||||
if (split.length == 1) {
|
if (split.length == 1) {
|
||||||
return executors.keySet().stream()
|
return Optional.of(executors.keySet().stream()
|
||||||
.filter(cmd -> cmd.regionMatches(true, 0, command, 0, command.length()))
|
.filter(cmd -> cmd.regionMatches(true, 0, command, 0, command.length()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] actualArgs = Arrays.copyOfRange(split, 1, split.length);
|
String[] actualArgs = Arrays.copyOfRange(split, 1, split.length);
|
||||||
CommandExecutor executor = executors.get(command);
|
CommandExecutor executor = executors.get(command);
|
||||||
if (executor == null) {
|
if (executor == null) {
|
||||||
return ImmutableList.of();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return executor.suggest(invoker, actualArgs);
|
return Optional.of(executor.suggest(invoker, actualArgs));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Unable to invoke suggestions for command " + command + " for " + invoker, e);
|
throw new RuntimeException("Unable to invoke suggestions for command " + command + " for " + invoker, e);
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,34 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<List<String>> offers = VelocityServer.getServer().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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Unable to provide tab list completions for " + player.getUsername() + " for command '" + req.getCommand() + "'", e);
|
||||||
|
TabCompleteResponse response = new TabCompleteResponse();
|
||||||
|
response.setTransactionId(req.getTransactionId());
|
||||||
|
player.getConnection().write(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (packet instanceof PluginMessage) {
|
if (packet instanceof PluginMessage) {
|
||||||
handleClientPluginMessage((PluginMessage) packet);
|
handleClientPluginMessage((PluginMessage) packet);
|
||||||
return;
|
return;
|
||||||
|
@ -7,6 +7,7 @@ import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
|||||||
import org.jline.reader.*;
|
import org.jline.reader.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public final class VelocityConsole extends SimpleTerminalConsole {
|
public final class VelocityConsole extends SimpleTerminalConsole {
|
||||||
|
|
||||||
@ -21,11 +22,13 @@ public final class VelocityConsole extends SimpleTerminalConsole {
|
|||||||
return super.buildReader(builder
|
return super.buildReader(builder
|
||||||
.appName("Velocity")
|
.appName("Velocity")
|
||||||
.completer((reader, parsedLine, list) -> {
|
.completer((reader, parsedLine, list) -> {
|
||||||
List<String> offers = server.getCommandManager().offerSuggestions(server.getConsoleCommandInvoker(), parsedLine.line());
|
Optional<List<String>> offers = server.getCommandManager().offerSuggestions(server.getConsoleCommandInvoker(), parsedLine.line());
|
||||||
for (String offer : offers) {
|
if (offers.isPresent()) {
|
||||||
|
for (String offer : offers.get()) {
|
||||||
if (offer.isEmpty()) continue;
|
if (offer.isEmpty()) continue;
|
||||||
list.add(new Candidate(offer));
|
list.add(new Candidate(offer));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,9 @@ public enum StateRegistry {
|
|||||||
},
|
},
|
||||||
PLAY {
|
PLAY {
|
||||||
{
|
{
|
||||||
|
SERVERBOUND.register(TabCompleteRequest.class, TabCompleteRequest::new,
|
||||||
|
map(0x01, MINECRAFT_1_12_2),
|
||||||
|
map(0x05, MINECRAFT_1_13));
|
||||||
SERVERBOUND.register(Chat.class, Chat::new,
|
SERVERBOUND.register(Chat.class, Chat::new,
|
||||||
map(0x01, MINECRAFT_1_8),
|
map(0x01, MINECRAFT_1_8),
|
||||||
map(0x02, MINECRAFT_1_9),
|
map(0x02, MINECRAFT_1_9),
|
||||||
@ -65,6 +68,9 @@ public enum StateRegistry {
|
|||||||
map(0x0F, MINECRAFT_1_9),
|
map(0x0F, MINECRAFT_1_9),
|
||||||
map(0x0F, MINECRAFT_1_12),
|
map(0x0F, MINECRAFT_1_12),
|
||||||
map(0x0E, MINECRAFT_1_13));
|
map(0x0E, MINECRAFT_1_13));
|
||||||
|
CLIENTBOUND.register(TabCompleteResponse.class, TabCompleteResponse::new,
|
||||||
|
map(0x0E, MINECRAFT_1_12),
|
||||||
|
map(0x10, MINECRAFT_1_13));
|
||||||
CLIENTBOUND.register(PluginMessage.class, PluginMessage::new,
|
CLIENTBOUND.register(PluginMessage.class, PluginMessage::new,
|
||||||
map(0x3F, MINECRAFT_1_8),
|
map(0x3F, MINECRAFT_1_8),
|
||||||
map(0x18, MINECRAFT_1_9),
|
map(0x18, MINECRAFT_1_9),
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
package com.velocitypowered.proxy.protocol.packet;
|
||||||
|
|
||||||
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
|
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||||
|
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_13;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCommand(String command) {
|
||||||
|
this.command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAssumeCommand() {
|
||||||
|
return assumeCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAssumeCommand(boolean assumeCommand) {
|
||||||
|
this.assumeCommand = assumeCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHasPosition() {
|
||||||
|
return hasPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasPosition(boolean hasPosition) {
|
||||||
|
this.hasPosition = hasPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPosition(long position) {
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TabCompleteRequest{" +
|
||||||
|
"transactionId=" + transactionId +
|
||||||
|
", command='" + command + '\'' +
|
||||||
|
", assumeCommand=" + assumeCommand +
|
||||||
|
", hasPosition=" + hasPosition +
|
||||||
|
", position=" + position +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
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);
|
||||||
|
buf.writeBoolean(assumeCommand);
|
||||||
|
buf.writeBoolean(hasPosition);
|
||||||
|
if (hasPosition) {
|
||||||
|
buf.writeLong(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,128 @@
|
|||||||
|
package com.velocitypowered.proxy.protocol.packet;
|
||||||
|
|
||||||
|
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.TextComponent;
|
||||||
|
import net.kyori.text.serializer.ComponentSerializers;
|
||||||
|
|
||||||
|
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<Offer> 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<Offer> getOffers() {
|
||||||
|
return offers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TabCompleteResponse{" +
|
||||||
|
"transactionId=" + transactionId +
|
||||||
|
", start=" + start +
|
||||||
|
", length=" + length +
|
||||||
|
", 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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, Component tooltip) {
|
||||||
|
this.entry = entry;
|
||||||
|
this.tooltip = tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntry() {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Component getTooltip() {
|
||||||
|
return tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Offer{" +
|
||||||
|
"entry='" + entry + '\'' +
|
||||||
|
", tooltip=" + tooltip +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren