diff --git a/src/main/java/xyz/nifeather/morph/commands/MorphCommand.java b/src/main/java/xyz/nifeather/morph/commands/MorphCommand.java index 8af29ddc..607570d6 100644 --- a/src/main/java/xyz/nifeather/morph/commands/MorphCommand.java +++ b/src/main/java/xyz/nifeather/morph/commands/MorphCommand.java @@ -1,19 +1,15 @@ package xyz.nifeather.morph.commands; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.IntegerArgumentType; -import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.Commands; -import net.kyori.adventure.text.Component; -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.commands.arguments.ResourceLocationArgument; -import net.minecraft.commands.synchronization.SuggestionProviders; -import net.minecraft.resources.ResourceLocation; -import org.bukkit.Bukkit; +import io.papermc.paper.command.brigadier.argument.ArgumentTypes; +import net.kyori.adventure.key.Key; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import xiamomc.pluginbase.Annotations.Resolved; @@ -21,6 +17,7 @@ import xyz.nifeather.morph.MorphManager; import xyz.nifeather.morph.MorphPluginObject; import xyz.nifeather.morph.commands.brigadier.IConvertibleBrigadier; +import xyz.nifeather.morph.commands.brigadier.arguments.ValueMapArgumentType; import xyz.nifeather.morph.messages.HelpStrings; import xyz.nifeather.morph.messages.MessageUtils; import xyz.nifeather.morph.messages.MorphStrings; @@ -28,8 +25,8 @@ import xyz.nifeather.morph.misc.gui.DisguiseSelectScreenWrapper; import java.util.concurrent.CompletableFuture; -import java.util.function.IntFunction; +@SuppressWarnings("UnstableApiUsage") public class MorphCommand extends MorphPluginObject implements IConvertibleBrigadier { @Resolved @@ -51,15 +48,31 @@ public boolean register(Commands dispatcher) .requires(this::checkPermission) .executes(this::executeNoArg) .then( - Commands.argument("id", ResourceLocationArgument.id()) + Commands.argument("id", ArgumentTypes.key()) .suggests(this::suggestID) .executes(this::execWithID) + //.then( + // Commands.argument("properties", new ValueMapArgumentType()) + // .executes(this::execExperimental) + //) ) .build()); return true; } + private int execExperimental(CommandContext context) + { + var input = ValueMapArgumentType.get("properties", context); + + input.forEach((k, v) -> + { + context.getSource().getSender().sendMessage("Key '%s', Value '%s'".formatted(k, v)); + }); + + return 1; + } + public @NotNull CompletableFuture suggestID(CommandContext context, SuggestionsBuilder suggestionsBuilder) { var source = context.getSource().getExecutor(); @@ -123,7 +136,7 @@ private int execWithID(CommandContext context) return Command.SINGLE_SUCCESS; } - String inputID = context.getArgument("id", ResourceLocation.class).toString(); + String inputID = context.getArgument("id", Key.class).toString(); morphManager.morph(sender, player, inputID, player.getTargetEntity(5)); return 1; diff --git a/src/main/java/xyz/nifeather/morph/commands/brigadier/arguments/ValueMapArgumentType.java b/src/main/java/xyz/nifeather/morph/commands/brigadier/arguments/ValueMapArgumentType.java new file mode 100644 index 00000000..d994f04f --- /dev/null +++ b/src/main/java/xyz/nifeather/morph/commands/brigadier/arguments/ValueMapArgumentType.java @@ -0,0 +1,126 @@ +package xyz.nifeather.morph.commands.brigadier.arguments; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.argument.CustomArgumentType; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import xyz.nifeather.morph.FeatherMorphMain; + +import java.util.Collection; +import java.util.Map; + +@SuppressWarnings("UnstableApiUsage") +public class ValueMapArgumentType implements CustomArgumentType, String> +{ + public static final Collection EXAMPLES = ObjectArrayList.of("[foo=bar]", "[foo=bar, aabb=\"ccdd\"]"); + private static final Logger log = FeatherMorphMain.getInstance().getSLF4JLogger(); + private static final Map defaultMap = new Object2ObjectOpenHashMap<>(); + + public static Map get(String name, CommandContext context) + { + return context.getArgument(name, defaultMap.getClass()); + } + + @Override + public Map parse(StringReader reader) throws CommandSyntaxException + { + if (reader.peek() != '[') + throw new SimpleCommandExceptionType(Component.translatable("Expected square bracket to start a string")).createWithContext(reader); + + if (!reader.getString().endsWith("]")) + throw new SimpleCommandExceptionType(Component.literal("Unclosed bracket string")).createWithContext(reader); + + reader.skip(); + + Map map = new Object2ObjectOpenHashMap<>(); + + while (reader.canRead()) + { + var subString = readStringQuotableUntil(reader, ','); + + if (subString == null) continue; + + String[] split = subString.split("=", 2); + String key = split[0]; + String value = split.length > 1 ? split[1] : null; + + if (value != null) + map.put(key, value); + } + + return map; + } + + /** + * @return A string, NULL if the input equals 'terminator' + * @throws CommandSyntaxException + */ + @Nullable + public String readStringQuotableUntil(StringReader reader, char terminator) throws CommandSyntaxException + { + //log.info("Starting read... Peek is '%s'".formatted(reader.peek())); + StringBuilder builder = new StringBuilder(); + + while (true) + { + if (!reader.canRead()) + break; + + char current = reader.read(); + //log.info("Current: '%s'".formatted(current)); + + // 遇到了结束符 + if (current == terminator) + break; + + // 遇到引号了 + if (StringReader.isQuotedStringStart(current)) + { + var str = reader.readStringUntil(current); + + //log.info("APPENDING QUOTE STRING [%s]".formatted(str)); + builder.append(str); + continue; + } + + // 如果遇到了闭合括号,break; + if (current == ']') + break; + + // 是空格 + if (Character.isWhitespace(current)) + continue; + + // 其他情况 + //log.info("APPENDING [%s]".formatted(current)); + builder.append(current); + } + + //log.info("DONE! result is [%s]".formatted(builder.toString())); + return builder.isEmpty() ? null : builder.toString(); + } + + @Override + @NotNull + public ArgumentType getNativeType() + { + return StringArgumentType.greedyString(); + } + + @Override + @NotNull + public Collection getExamples() + { + return EXAMPLES; + } +}