mirror of
https://github.com/LeavesMC/Leaves.git
synced 2025-12-31 04:46:29 +00:00
feat: neo command
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MC_XiaoHei <xor7xiaohei@gmail.com>
|
||||
Date: Fri, 22 Aug 2025 09:42:16 +0800
|
||||
Subject: [PATCH] Leaves Server Command
|
||||
|
||||
|
||||
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
|
||||
index ec1cced129ef42be65d7b2b622638bfae8bd895e..b54b1b56c8df6c6e03c2f53423b273a7c975a498 100644
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -182,6 +182,7 @@ public class Commands {
|
||||
}
|
||||
public Commands(Commands.CommandSelection selection, CommandBuildContext context, final boolean modern) {
|
||||
// Paper end - Brigadier API - modern minecraft overloads that do not use redirects but are copies instead
|
||||
+ org.leavesmc.leaves.neo_command.LeavesCommands.registerLeavesCommands(dispatcher); // Leaves Commands
|
||||
AdvancementCommands.register(this.dispatcher);
|
||||
AttributeCommand.register(this.dispatcher, context);
|
||||
ExecuteCommand.register(this.dispatcher, context);
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.leavesmc.leaves.neo_command;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public abstract class ArgumentNode<T> extends CommandNode {
|
||||
private final ArgumentType<T> argumentType;
|
||||
|
||||
protected ArgumentNode(String name, ArgumentType<T> argumentType) {
|
||||
super(name);
|
||||
this.argumentType = argumentType;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unused", "RedundantThrows"})
|
||||
protected CompletableFuture<Suggestions> getSuggestions(final CommandContext context, final SuggestionsBuilder builder) throws CommandSyntaxException {
|
||||
return Suggestions.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ArgumentBuilder<CommandSourceStack, ?> compileBase() {
|
||||
RequiredArgumentBuilder<CommandSourceStack, T> argumentBuilder = Commands.argument(name, argumentType);
|
||||
if (isMethodOverridden("getSuggestions", ArgumentNode.class)) {
|
||||
argumentBuilder.suggests(
|
||||
(context, builder) -> getSuggestions(new CommandContext(context), builder)
|
||||
);
|
||||
}
|
||||
return argumentBuilder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package org.leavesmc.leaves.neo_command;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.RedirectModifier;
|
||||
import com.mojang.brigadier.context.ParsedCommandNode;
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.leavesmc.leaves.neo_command.CommandNode.*;
|
||||
|
||||
@SuppressWarnings({"ClassCanBeRecord", "unused"})
|
||||
public class CommandContext {
|
||||
private final com.mojang.brigadier.context.CommandContext<CommandSourceStack> source;
|
||||
|
||||
public CommandContext(com.mojang.brigadier.context.CommandContext<CommandSourceStack> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public com.mojang.brigadier.context.CommandContext<CommandSourceStack> getChild() {
|
||||
return source.getChild();
|
||||
}
|
||||
|
||||
public com.mojang.brigadier.context.CommandContext<CommandSourceStack> getLastChild() {
|
||||
return source.getLastChild();
|
||||
}
|
||||
|
||||
public Command<CommandSourceStack> getCommand() {
|
||||
return source.getCommand();
|
||||
}
|
||||
|
||||
public CommandSourceStack getSource() {
|
||||
return source.getSource();
|
||||
}
|
||||
|
||||
public CommandSender getSender() {
|
||||
return source.getSource().getSender();
|
||||
}
|
||||
|
||||
public <V> @NotNull V getArgument(final String name, final Class<V> clazz) {
|
||||
return source.getArgument(name, clazz);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> @NotNull V getArgument(final Class<? extends ArgumentNode<V>> nodeClass) {
|
||||
String name = getNameForNode(nodeClass);
|
||||
return (V) source.getArgument(name, Object.class);
|
||||
}
|
||||
|
||||
public <V> @NotNull V getArgumentOrDefault(final Class<? extends ArgumentNode<V>> nodeClass, final V defaultValue) {
|
||||
try {
|
||||
return getArgument(nodeClass);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public <V> V getArgumentOrDefault(final String name, final Class<V> clazz, final V defaultValue) {
|
||||
try {
|
||||
return source.getArgument(name, clazz);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public String getStringOrDefault(final String name, final String defaultValue) {
|
||||
return getArgumentOrDefault(name, String.class, defaultValue);
|
||||
}
|
||||
|
||||
public int getIntegerOrDefault(final String name, final int defaultValue) {
|
||||
return getArgumentOrDefault(name, Integer.class, defaultValue);
|
||||
}
|
||||
|
||||
public RedirectModifier<CommandSourceStack> getRedirectModifier() {
|
||||
return source.getRedirectModifier();
|
||||
}
|
||||
|
||||
public StringRange getRange() {
|
||||
return source.getRange();
|
||||
}
|
||||
|
||||
public String getInput() {
|
||||
return source.getInput();
|
||||
}
|
||||
|
||||
public CommandNode<CommandSourceStack> getRootNode() {
|
||||
return source.getRootNode();
|
||||
}
|
||||
|
||||
public List<ParsedCommandNode<CommandSourceStack>> getNodes() {
|
||||
return source.getNodes();
|
||||
}
|
||||
|
||||
public boolean hasNodes() {
|
||||
return source.hasNodes();
|
||||
}
|
||||
|
||||
public boolean isForked() {
|
||||
return source.isForked();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.leavesmc.leaves.neo_command;
|
||||
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class CommandNode {
|
||||
private static final Map<Class<? extends CommandNode>, String> class2NameMap = new HashMap<>();
|
||||
|
||||
protected final String name;
|
||||
protected final List<CommandNode> children = new ArrayList<>();
|
||||
|
||||
protected CommandNode(String name) {
|
||||
this.name = name;
|
||||
class2NameMap.put(getClass(), name);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
protected final void children(Supplier<? extends CommandNode>... childrenClasses) {
|
||||
this.children.addAll(Stream.of(childrenClasses).map(Supplier::get).toList());
|
||||
}
|
||||
|
||||
protected abstract ArgumentBuilder<CommandSourceStack, ?> compileBase();
|
||||
|
||||
protected boolean execute(CommandContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean requires(CommandSourceStack source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected ArgumentBuilder<CommandSourceStack, ?> compile() {
|
||||
ArgumentBuilder<CommandSourceStack, ?> builder = compileBase();
|
||||
|
||||
if (isMethodOverridden("requires", CommandNode.class)) {
|
||||
builder = builder.requires(this::requires);
|
||||
}
|
||||
|
||||
for (CommandNode child : children) {
|
||||
builder = builder.then(child.compile());
|
||||
}
|
||||
|
||||
if (isMethodOverridden("execute", CommandNode.class)) {
|
||||
builder = builder.executes(mojangCtx -> {
|
||||
CommandContext ctx = new CommandContext(mojangCtx);
|
||||
return execute(ctx) ? 1 : 0;
|
||||
});
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected boolean isMethodOverridden(String methodName, @NotNull Class<?> baseClass) {
|
||||
for (Method method : getClass().getDeclaredMethods()) {
|
||||
if (method.getName().equals(methodName)) {
|
||||
return method.getDeclaringClass() != baseClass;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getNameForNode(Class<? extends CommandNode> nodeClass) {
|
||||
return class2NameMap.get(nodeClass);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.leavesmc.leaves.neo_command;
|
||||
|
||||
import org.leavesmc.leaves.neo_command.subcommands.ConfigCommand;
|
||||
|
||||
public class LeavesCommand extends LiteralNode {
|
||||
public LeavesCommand() {
|
||||
super("leaves_new");
|
||||
children(ConfigCommand::new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.leavesmc.leaves.neo_command;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LeavesCommands {
|
||||
public static void registerLeavesCommands(@NotNull CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||
new LeavesCommand().register(dispatcher);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.leavesmc.leaves.neo_command;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LiteralNode extends CommandNode {
|
||||
|
||||
protected LiteralNode(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ArgumentBuilder<CommandSourceStack, ?> compileBase() {
|
||||
return Commands.literal(name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void register(@NotNull CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||
dispatcher.register((LiteralArgumentBuilder<CommandSourceStack>) compile());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package org.leavesmc.leaves.neo_command.subcommands;
|
||||
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.JoinConfiguration;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.command.LeavesCommandUtil;
|
||||
import org.leavesmc.leaves.config.GlobalConfigManager;
|
||||
import org.leavesmc.leaves.config.VerifiedConfig;
|
||||
import org.leavesmc.leaves.neo_command.ArgumentNode;
|
||||
import org.leavesmc.leaves.neo_command.CommandContext;
|
||||
import org.leavesmc.leaves.neo_command.LiteralNode;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
|
||||
public class ConfigCommand extends LiteralNode {
|
||||
|
||||
public ConfigCommand() {
|
||||
super("config");
|
||||
children(PathArgument::new);
|
||||
}
|
||||
|
||||
private static class PathArgument extends ArgumentNode<String> {
|
||||
|
||||
public PathArgument() {
|
||||
super("path", StringArgumentType.string());
|
||||
children(ValueArgument::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Suggestions> getSuggestions(@NotNull CommandContext context, @NotNull SuggestionsBuilder builder) {
|
||||
String path = context.getArgumentOrDefault(PathArgument.class, "");
|
||||
int dotIndex = path.lastIndexOf(".");
|
||||
builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + dotIndex + 2);
|
||||
LeavesCommandUtil.getListClosestMatchingLast(
|
||||
context.getSender(),
|
||||
path.substring(dotIndex + 1),
|
||||
GlobalConfigManager.getVerifiedConfigSubPaths(path),
|
||||
"bukkit.command.leaves.config"
|
||||
)
|
||||
.forEach(builder::suggest);
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean execute(@NotNull CommandContext context) {
|
||||
String path = context.getArgument(PathArgument.class);
|
||||
VerifiedConfig verifiedConfig = getVerifiedConfig(context);
|
||||
if (verifiedConfig == null) {
|
||||
return false;
|
||||
}
|
||||
context.getSender().sendMessage(Component.join(JoinConfiguration.spaces(),
|
||||
text("Config", NamedTextColor.GRAY),
|
||||
text(path, NamedTextColor.AQUA),
|
||||
text("value is", NamedTextColor.GRAY),
|
||||
text(verifiedConfig.getString(), NamedTextColor.AQUA)
|
||||
));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static @Nullable VerifiedConfig getVerifiedConfig(@NotNull CommandContext context) {
|
||||
String path = context.getArgument(PathArgument.class);
|
||||
VerifiedConfig verifiedConfig = GlobalConfigManager.getVerifiedConfig(path);
|
||||
if (verifiedConfig == null) {
|
||||
context.getSender().sendMessage(Component.join(JoinConfiguration.spaces(),
|
||||
text("Config", NamedTextColor.GRAY),
|
||||
text(path, NamedTextColor.RED),
|
||||
text("is Not Found.", NamedTextColor.GRAY)
|
||||
));
|
||||
return null;
|
||||
}
|
||||
return verifiedConfig;
|
||||
}
|
||||
|
||||
private static class ValueArgument extends ArgumentNode<String> {
|
||||
|
||||
public ValueArgument() {
|
||||
super("value", StringArgumentType.greedyString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Suggestions> getSuggestions(@NotNull CommandContext context, @NotNull SuggestionsBuilder builder) {
|
||||
String path = context.getArgument(PathArgument.class);
|
||||
String value = context.getArgumentOrDefault(ValueArgument.class, "");
|
||||
VerifiedConfig verifiedConfig = GlobalConfigManager.getVerifiedConfig(path);
|
||||
if (verifiedConfig == null) {
|
||||
return builder
|
||||
.suggest("<ERROR CONFIG>", net.minecraft.network.chat.Component.literal("This config path does not exist."))
|
||||
.buildFuture();
|
||||
}
|
||||
LeavesCommandUtil.getListMatchingLast(
|
||||
context.getSender(),
|
||||
new String[]{value},
|
||||
verifiedConfig.validator().valueSuggest()
|
||||
).forEach(builder::suggest);
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean execute(@NotNull CommandContext context) {
|
||||
VerifiedConfig verifiedConfig = getVerifiedConfig(context);
|
||||
String path = context.getArgument(PathArgument.class);
|
||||
String value = context.getArgument(ValueArgument.class);
|
||||
if (verifiedConfig == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
verifiedConfig.set(value);
|
||||
context.getSender().sendMessage(Component.join(JoinConfiguration.spaces(),
|
||||
text("Config", NamedTextColor.GRAY),
|
||||
text(path, NamedTextColor.AQUA),
|
||||
text("changed to", NamedTextColor.GRAY),
|
||||
text(verifiedConfig.getString(), NamedTextColor.AQUA)
|
||||
));
|
||||
Bukkit.getOnlinePlayers()
|
||||
.stream()
|
||||
.filter(player -> player.hasPermission("leaves.command.config.notify") && player != context.getSender())
|
||||
.forEach(
|
||||
player -> player.sendMessage(Component.join(JoinConfiguration.spaces(),
|
||||
text(context.getSender().getName() + ":", NamedTextColor.GRAY),
|
||||
text("Config", NamedTextColor.GRAY),
|
||||
text(path, NamedTextColor.AQUA),
|
||||
text("changed to", NamedTextColor.GRAY),
|
||||
text(verifiedConfig.getString(), NamedTextColor.AQUA)
|
||||
))
|
||||
);
|
||||
return true;
|
||||
} catch (IllegalArgumentException exception) {
|
||||
context.getSender().sendMessage(Component.join(JoinConfiguration.spaces(),
|
||||
text("Config", NamedTextColor.GRAY),
|
||||
text(path, NamedTextColor.RED),
|
||||
text("modify error by", NamedTextColor.GRAY),
|
||||
text(exception.getMessage(), NamedTextColor.RED)
|
||||
));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user