mirror of https://github.com/Minestom/Minestom.git
Initial command suggestion commit
This commit is contained in:
parent
7241dbdcf7
commit
441cb5a1db
|
@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
|
|||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.command.builder.*;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
|
||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.player.PlayerCommandEvent;
|
||||
|
@ -497,6 +498,14 @@ public final class CommandManager {
|
|||
nodeMaker.getNodeIdsMap().put(argumentNode, childId);
|
||||
argChildren.add(childId);
|
||||
|
||||
// Enable ASK_SERVER suggestion if required
|
||||
{
|
||||
if (argument.hasSuggestion()) {
|
||||
argumentNode.flags |= 0x10; // Suggestion flag
|
||||
argumentNode.suggestionsType = SuggestionType.ASK_SERVER.getIdentifier();
|
||||
}
|
||||
}
|
||||
|
||||
// Append to the last node
|
||||
{
|
||||
final int[] children = ArrayUtils.toArray(argChildren);
|
||||
|
|
|
@ -5,6 +5,7 @@ import net.minestom.server.command.builder.Command;
|
|||
import net.minestom.server.command.builder.CommandExecutor;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.command.builder.suggestion.SuggestionCallback;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -28,6 +29,8 @@ public abstract class Argument<T> {
|
|||
|
||||
private T defaultValue;
|
||||
|
||||
public SuggestionCallback suggestionCallback;
|
||||
|
||||
/**
|
||||
* Creates a new argument.
|
||||
*
|
||||
|
@ -148,6 +151,15 @@ public abstract class Argument<T> {
|
|||
this.callback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the argument has any error callback.
|
||||
*
|
||||
* @return true if the argument has an error callback, false otherwise
|
||||
*/
|
||||
public boolean hasErrorCallback() {
|
||||
return callback != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if this argument is 'optional'.
|
||||
* <p>
|
||||
|
@ -185,13 +197,18 @@ public abstract class Argument<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the argument has any error callback.
|
||||
*
|
||||
* @return true if the argument has an error callback, false otherwise
|
||||
*/
|
||||
public boolean hasErrorCallback() {
|
||||
return callback != null;
|
||||
@Nullable
|
||||
public SuggestionCallback getSuggestionCallback() {
|
||||
return suggestionCallback;
|
||||
}
|
||||
|
||||
public Argument<T> setSuggestionCallback(@NotNull SuggestionCallback suggestionCallback) {
|
||||
this.suggestionCallback = suggestionCallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasSuggestion() {
|
||||
return suggestionCallback != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
* Same as {@link ArgumentStringArray} with the exception
|
||||
* that this argument can trigger {@link net.minestom.server.command.builder.Command#onDynamicWrite(CommandSender, String)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ArgumentDynamicStringArray extends Argument<String[]> {
|
||||
|
||||
public static final int RESTRICTION_ERROR = 1;
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
* that this argument can trigger {@link net.minestom.server.command.builder.Command#onDynamicWrite(CommandSender, String)}
|
||||
* when the suggestion type is {@link SuggestionType#ASK_SERVER}, or any other suggestions available in the enum.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ArgumentDynamicWord extends Argument<String> {
|
||||
|
||||
public static final int SPACE_ERROR = 1;
|
||||
|
|
|
@ -23,7 +23,7 @@ public class ArgumentEnum<E extends Enum> extends Argument<E> {
|
|||
this.values = enumClass.getEnumConstants();
|
||||
}
|
||||
|
||||
public ArgumentEnum setFormat(@NotNull Format format) {
|
||||
public ArgumentEnum<E> setFormat(@NotNull Format format) {
|
||||
this.format = format;
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,8 @@ public class ArgumentString extends Argument<String> {
|
|||
// Check if value start and end with quote
|
||||
final char first = input.charAt(0);
|
||||
final char last = input.charAt(input.length() - 1);
|
||||
final boolean quote = first == StringUtil.DOUBLE_QUOTE && last == StringUtil.DOUBLE_QUOTE;
|
||||
final boolean quote = input.length() >= 2 &&
|
||||
first == StringUtil.DOUBLE_QUOTE && last == StringUtil.DOUBLE_QUOTE;
|
||||
if (!quote)
|
||||
throw new ArgumentSyntaxException("String argument needs to start and end with quotes", input, QUOTE_ERROR);
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package net.minestom.server.command.builder.suggestion;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Suggestion {
|
||||
|
||||
private int start;
|
||||
private int length;
|
||||
private final List<SuggestionEntry> suggestionEntries = new ArrayList<>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<SuggestionEntry> getEntries() {
|
||||
return suggestionEntries;
|
||||
}
|
||||
|
||||
public void addEntry(@NotNull SuggestionEntry entry) {
|
||||
this.suggestionEntries.add(entry);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package net.minestom.server.command.builder.suggestion;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SuggestionCallback {
|
||||
void apply(@NotNull Suggestion suggestion);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package net.minestom.server.command.builder.suggestion;
|
||||
|
||||
import net.minestom.server.chat.JsonMessage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SuggestionEntry {
|
||||
|
||||
private final String entry;
|
||||
private final JsonMessage tooltip;
|
||||
|
||||
public SuggestionEntry(@NotNull String entry, @Nullable JsonMessage tooltip) {
|
||||
this.entry = entry;
|
||||
this.tooltip = tooltip;
|
||||
}
|
||||
|
||||
public SuggestionEntry(@NotNull String entry) {
|
||||
this(entry, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getEntry() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JsonMessage getTooltip() {
|
||||
return tooltip;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,27 @@
|
|||
package net.minestom.server.listener;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.command.CommandManager;
|
||||
import net.minestom.server.command.CommandProcessor;
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandDispatcher;
|
||||
import net.minestom.server.command.builder.CommandSyntax;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.parser.CommandParser;
|
||||
import net.minestom.server.command.builder.parser.CommandSuggestionHolder;
|
||||
import net.minestom.server.command.builder.parser.ValidSyntaxHolder;
|
||||
import net.minestom.server.command.builder.suggestion.Suggestion;
|
||||
import net.minestom.server.command.builder.suggestion.SuggestionCallback;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.network.packet.client.play.ClientTabCompletePacket;
|
||||
import net.minestom.server.network.packet.server.play.TabCompletePacket;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TabCompleteListener {
|
||||
|
@ -18,6 +31,62 @@ public class TabCompleteListener {
|
|||
public static void listener(ClientTabCompletePacket packet, Player player) {
|
||||
final String text = packet.text;
|
||||
|
||||
{
|
||||
String commandString = packet.text.replaceFirst("/", "");
|
||||
String[] split = commandString.split(StringUtils.SPACE);
|
||||
String commandName = split[0];
|
||||
final CommandDispatcher commandDispatcher = MinecraftServer.getCommandManager().getDispatcher();
|
||||
final Command command = commandDispatcher.findCommand(commandName);
|
||||
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
|
||||
List<ValidSyntaxHolder> validSyntaxes = new ArrayList<>(syntaxes.size());
|
||||
Int2ObjectRBTreeMap<CommandSuggestionHolder> syntaxesSuggestions = new Int2ObjectRBTreeMap<>(Collections.reverseOrder());
|
||||
|
||||
String[] args = commandString.replaceFirst(Pattern.quote(commandName), "").trim().split(StringUtils.SPACE);
|
||||
if (args.length == 1 && args[0].length() == 0) {
|
||||
args = new String[0];
|
||||
}
|
||||
|
||||
for (CommandSyntax syntax : syntaxes) {
|
||||
CommandParser.parse(syntax, syntax.getArguments(), args, validSyntaxes, syntaxesSuggestions);
|
||||
}
|
||||
|
||||
if (!syntaxesSuggestions.isEmpty()) {
|
||||
final int max = syntaxesSuggestions.firstIntKey();
|
||||
final CommandSuggestionHolder suggestionHolder = syntaxesSuggestions.get(max);
|
||||
final CommandSyntax syntax = suggestionHolder.syntax;
|
||||
final int argIndex = suggestionHolder.argIndex;
|
||||
final Argument<?> argument = syntax.getArguments()[argIndex];
|
||||
|
||||
final SuggestionCallback suggestionCallback = argument.suggestionCallback;
|
||||
if (suggestionCallback != null) {
|
||||
Suggestion suggestion = new Suggestion();
|
||||
suggestionCallback.apply(suggestion);
|
||||
|
||||
TabCompletePacket tabCompletePacket = new TabCompletePacket();
|
||||
tabCompletePacket.transactionId = packet.transactionId;
|
||||
tabCompletePacket.start = suggestion.getStart();
|
||||
tabCompletePacket.length = suggestion.getLength();
|
||||
tabCompletePacket.matches = suggestion.getEntries()
|
||||
.stream()
|
||||
.map(suggestionEntry -> {
|
||||
TabCompletePacket.Match match = new TabCompletePacket.Match();
|
||||
match.match = suggestionEntry.getEntry();
|
||||
match.hasTooltip = suggestionEntry.getTooltip() != null;
|
||||
match.tooltip = suggestionEntry.getTooltip();
|
||||
return match;
|
||||
}).toArray(TabCompletePacket.Match[]::new);
|
||||
|
||||
player.getPlayerConnection().sendPacket(tabCompletePacket);
|
||||
}
|
||||
System.out.println("arg: " + argument.getClass());
|
||||
}
|
||||
|
||||
System.out.println("test " + syntaxesSuggestions.size() + " " + validSyntaxes.size());
|
||||
|
||||
if (true)
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] split = packet.text.split(Pattern.quote(StringUtils.SPACE));
|
||||
|
||||
final String commandName = split[0].replaceFirst(CommandManager.COMMAND_PREFIX, "");
|
||||
|
|
|
@ -31,7 +31,7 @@ public class Main {
|
|||
|
||||
CommandManager commandManager = MinecraftServer.getCommandManager();
|
||||
commandManager.register(new TestCommand());
|
||||
commandManager.register(new GamemodeCommand());
|
||||
/*commandManager.register(new GamemodeCommand());
|
||||
commandManager.register(new EntitySelectorCommand());
|
||||
commandManager.register(new HealthCommand());
|
||||
commandManager.register(new SimpleCommand());
|
||||
|
@ -43,7 +43,7 @@ public class Main {
|
|||
commandManager.register(new TitleCommand());
|
||||
commandManager.register(new BookCommand());
|
||||
commandManager.register(new ShootCommand());
|
||||
commandManager.register(new HorseCommand());
|
||||
commandManager.register(new HorseCommand());*/
|
||||
|
||||
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage("unknown command"));
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package demo.commands;
|
||||
|
||||
import net.minestom.server.chat.JsonMessage;
|
||||
import net.minestom.server.chat.ChatColor;
|
||||
import net.minestom.server.chat.ColoredText;
|
||||
import net.minestom.server.command.CommandSender;
|
||||
import net.minestom.server.command.builder.Arguments;
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.suggestion.SuggestionEntry;
|
||||
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.Component;
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.Integer;
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.String;
|
||||
|
||||
public class TestCommand extends Command {
|
||||
|
||||
|
@ -14,11 +15,12 @@ public class TestCommand extends Command {
|
|||
super("testcmd");
|
||||
setDefaultExecutor(this::usage);
|
||||
|
||||
var number = Integer("number2");
|
||||
|
||||
addSyntax((sender, args) -> {
|
||||
sender.sendMessage((JsonMessage) args.get("msg"));
|
||||
}, Component("msg"));
|
||||
System.out.println("test: " + args.get("msg"));
|
||||
}, String("msg").setSuggestionCallback(suggestion -> {
|
||||
suggestion.setLength(999);
|
||||
suggestion.addEntry(new SuggestionEntry("Match", ColoredText.of(ChatColor.RED, "Hover")));
|
||||
}));
|
||||
}
|
||||
|
||||
private void usage(CommandSender sender, Arguments arguments) {
|
||||
|
|
Loading…
Reference in New Issue