From 8daca5be99177805c01f4a15b4eba1c00095febb Mon Sep 17 00:00:00 2001 From: joe Date: Wed, 27 Nov 2024 22:43:29 +0000 Subject: [PATCH] Improvements to cvillager command --- .../command/VillagerCommand.java | 104 ++++++++++-------- .../ItemAndEnchantmentsPredicateArgument.java | 17 ++- .../command/arguments/WithStringArgument.java | 8 +- .../features/VillagerCracker.java | 19 +++- .../assets/clientcommands/lang/en_us.json | 1 - 5 files changed, 96 insertions(+), 53 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/VillagerCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/VillagerCommand.java index f0e56c9a5..cdbac1e48 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/VillagerCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/VillagerCommand.java @@ -5,8 +5,9 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import dev.xpple.clientarguments.arguments.CRangeArgument; import net.earthcomputer.clientcommands.Configs; +import net.earthcomputer.clientcommands.command.arguments.ClientItemPredicateArgument; +import net.earthcomputer.clientcommands.command.arguments.ItemAndEnchantmentsPredicateArgument; import net.earthcomputer.clientcommands.command.arguments.WithStringArgument; import net.earthcomputer.clientcommands.features.VillagerCracker; import net.earthcomputer.clientcommands.features.VillagerRngSimulator; @@ -31,14 +32,13 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.function.Predicate; import java.util.stream.Collectors; import static com.mojang.brigadier.arguments.IntegerArgumentType.*; import static dev.xpple.clientarguments.arguments.CBlockPosArgument.*; import static dev.xpple.clientarguments.arguments.CEntityArgument.*; -import static dev.xpple.clientarguments.arguments.CItemPredicateArgument.*; import static dev.xpple.clientarguments.arguments.CRangeArgument.*; +import static net.earthcomputer.clientcommands.command.arguments.ClientItemPredicateArgument.*; import static net.earthcomputer.clientcommands.command.arguments.ItemAndEnchantmentsPredicateArgument.*; import static net.earthcomputer.clientcommands.command.arguments.WithStringArgument.*; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; @@ -50,37 +50,35 @@ public class VillagerCommand { private static final SimpleCommandExceptionType NOT_LEVEL_1_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.cvillager.notLevel1")); private static final SimpleCommandExceptionType NO_GOALS_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.cvillager.listGoals.noGoals")); private static final SimpleCommandExceptionType ALREADY_RUNNING_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.cvillager.alreadyRunning")); - private static final Dynamic2CommandExceptionType INVALID_GOAL_INDEX_EXCEPTION = new Dynamic2CommandExceptionType((a, b) -> Component.translatable("commands.cvillager.removeGoal.invalidIndex", a, b)); - private static final Dynamic2CommandExceptionType ITEM_QUANTITY_OUT_OF_RANGE_EXCEPTION = new Dynamic2CommandExceptionType((a, b) -> Component.translatable("commands.cvillager.itemCountOutOfRange", a, b)); + private static final Dynamic2CommandExceptionType INVALID_GOAL_INDEX_EXCEPTION = new Dynamic2CommandExceptionType((index, length) -> Component.translatable("commands.cvillager.removeGoal.invalidIndex", index, length)); + private static final Dynamic2CommandExceptionType ITEM_OVERSTACKED_EXCEPTION = new Dynamic2CommandExceptionType((item, stackSize) -> Component.translatable("arguments.item.overstacked", item, stackSize)); public static void register(CommandDispatcher dispatcher, CommandBuildContext context) { dispatcher.register(literal("cvillager") - .then(literal("add-two-item-goal") - .then(argument("first-item", withString(itemPredicate(context))) - .then(argument("first-count", intRange()) - .then(argument("result-item", withString(itemPredicate(context))) - .then(argument("result-count", intRange()) - .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "first-item", CItemStackPredicateArgument.class), CRangeArgument.Ints.getRangeArgument(ctx, "first-count"), null, null, getWithString(ctx, "result-item", CItemStackPredicateArgument.class), CRangeArgument.Ints.getRangeArgument(ctx, "result-count")))))))) - .then(literal("add-three-item-goal") - .then(argument("first-item", withString(itemPredicate(context))) - .then(argument("first-count", intRange()) - .then(argument("second-item", withString(itemPredicate(context))) - .then(argument("second-count", intRange()) - .then(argument("result-item", withString(itemPredicate(context))) - .then(argument("result-count", intRange()) - .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "first-item", CItemStackPredicateArgument.class), CRangeArgument.Ints.getRangeArgument(ctx, "first-count"), getWithString(ctx, "second-item", CItemStackPredicateArgument.class), CRangeArgument.Ints.getRangeArgument(ctx, "second-count"), getWithString(ctx, "result-item", CItemStackPredicateArgument.class), CRangeArgument.Ints.getRangeArgument(ctx, "result-count")))))))))) - .then(literal("add-two-item-enchanted-goal") - .then(argument("first-item", withString(itemPredicate(context))) - .then(argument("first-count", intRange()) - .then(argument("result-item", withString(itemAndEnchantmentsPredicate(context).withEnchantmentPredicate((item, enchantment) -> enchantment.is(EnchantmentTags.TRADEABLE)))) - .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "first-item", CItemStackPredicateArgument.class), CRangeArgument.Ints.getRangeArgument(ctx, "first-count"), null, null, getWithString(ctx, "result-item", ItemAndEnchantmentsPredicate.class), MinMaxBounds.Ints.between(1, 1))))))) - .then(literal("add-three-item-enchanted-goal") - .then(argument("first-item", withString(itemPredicate(context))) - .then(argument("first-count", intRange()) - .then(argument("second-item", withString(itemPredicate(context))) - .then(argument("second-count", intRange()) - .then(argument("result-item", withString(itemAndEnchantmentsPredicate(context).withEnchantmentPredicate((item, enchantment) -> enchantment.is(EnchantmentTags.TRADEABLE)))) - .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "first-item", CItemStackPredicateArgument.class), CRangeArgument.Ints.getRangeArgument(ctx, "first-count"), getWithString(ctx, "second-item", CItemStackPredicateArgument.class), CRangeArgument.Ints.getRangeArgument(ctx, "second-count"), getWithString(ctx, "result-item", ItemAndEnchantmentsPredicate.class), MinMaxBounds.Ints.between(1, 1))))))))) + .then(literal("add-goal") + .then(argument("result", withString(clientItemPredicate(context))) + .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "result", ClientItemPredicateArgument.ClientItemPredicate.class), MinMaxBounds.Ints.ANY, null, MinMaxBounds.Ints.ANY, null, MinMaxBounds.Ints.ANY)) + .then(argument("result-count", intRange()) + .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "result", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "result-count"), null, MinMaxBounds.Ints.ANY, null, MinMaxBounds.Ints.ANY)) + .then(argument("input", withString(clientItemPredicate(context))) + .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "result", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "result-count"), getWithString(ctx, "input", ClientItemPredicateArgument.ClientItemPredicate.class), MinMaxBounds.Ints.ANY, null, MinMaxBounds.Ints.ANY)) + .then(argument("input-count", intRange()) + .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "result", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "result-count"), getWithString(ctx, "input", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "input-count"), null, MinMaxBounds.Ints.ANY)) + .then(argument("second-input", withString(clientItemPredicate(context))) + .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "result", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "result-count"), getWithString(ctx, "input", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "input-count"), getWithString(ctx, "second-input", ClientItemPredicateArgument.ClientItemPredicate.class), MinMaxBounds.Ints.ANY)) + .then(argument("second-input-count", intRange()) + .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "result", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "result-count"), getWithString(ctx, "input", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "input-count"), getWithString(ctx, "second-input", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "second-input-count")))))))))) + .then(literal("add-enchanted-goal") + .then(argument("result", withString(itemAndEnchantmentsPredicate(context).withSuffix("from").withEnchantmentPredicate((item, ench) -> ench.is(EnchantmentTags.TRADEABLE)))) + .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "result", ItemAndEnchantmentsPredicateArgument.ItemAndEnchantmentsPredicate.class).map(ClientItemPredicateArgument.EnchantedItemPredicate::new), MinMaxBounds.Ints.ANY, null, MinMaxBounds.Ints.ANY, null, MinMaxBounds.Ints.ANY)) + .then(argument("input", withString(clientItemPredicate(context))) + .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "result", ItemAndEnchantmentsPredicateArgument.ItemAndEnchantmentsPredicate.class).map(ClientItemPredicateArgument.EnchantedItemPredicate::new), MinMaxBounds.Ints.ANY, getWithString(ctx, "input", ClientItemPredicateArgument.ClientItemPredicate.class), MinMaxBounds.Ints.ANY, null, MinMaxBounds.Ints.ANY)) + .then(argument("input-count", intRange()) + .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "result", ItemAndEnchantmentsPredicateArgument.ItemAndEnchantmentsPredicate.class).map(ClientItemPredicateArgument.EnchantedItemPredicate::new), MinMaxBounds.Ints.ANY, getWithString(ctx, "input", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "input-count"), null, MinMaxBounds.Ints.ANY)) + .then(argument("second-input", withString(clientItemPredicate(context))) + .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "result", ItemAndEnchantmentsPredicateArgument.ItemAndEnchantmentsPredicate.class).map(ClientItemPredicateArgument.EnchantedItemPredicate::new), MinMaxBounds.Ints.ANY, getWithString(ctx, "input", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "input-count"), getWithString(ctx, "second-input", ClientItemPredicateArgument.ClientItemPredicate.class), MinMaxBounds.Ints.ANY)) + .then(argument("second-input-count", intRange()) + .executes(ctx -> addGoal(ctx.getSource(), getWithString(ctx, "result", ItemAndEnchantmentsPredicateArgument.ItemAndEnchantmentsPredicate.class).map(ClientItemPredicateArgument.EnchantedItemPredicate::new), MinMaxBounds.Ints.ANY, getWithString(ctx, "input", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "input-count"), getWithString(ctx, "second-input", ClientItemPredicateArgument.ClientItemPredicate.class), Ints.getRangeArgument(ctx, "second-input-count"))))))))) .then(literal("list-goals") .executes(ctx -> listGoals(ctx.getSource()))) .then(literal("remove-goal") @@ -106,32 +104,50 @@ public static void register(CommandDispatcher dispatc private static int addGoal( FabricClientCommandSource ctx, - WithStringArgument.Result> first, + Result result, + MinMaxBounds.Ints resultCount, + @Nullable WithStringArgument.Result first, MinMaxBounds.Ints firstCount, - @Nullable WithStringArgument.Result> second, - @Nullable MinMaxBounds.Ints secondCount, - Result> result, - MinMaxBounds.Ints resultCount - ) { - String firstString = first.string() + " " + CUtil.boundsToString(firstCount); - String secondString = second == null || secondCount == null ? null : second.string() + " " + CUtil.boundsToString(secondCount); + @Nullable WithStringArgument.Result second, + MinMaxBounds.Ints secondCount + ) throws CommandSyntaxException { + checkStackSize(result, resultCount); + if (first != null) { + checkStackSize(first, firstCount); + } + if (second != null) { + checkStackSize(second, secondCount); + } + + String firstString = first == null ? null : first.string() + " " + CUtil.boundsToString(firstCount); + String secondString = second == null ? null : second.string() + " " + CUtil.boundsToString(secondCount); String resultString = result.string() + " " + CUtil.boundsToString(resultCount); VillagerCracker.goals.add(new VillagerCracker.Goal( + resultString, + item -> result.value().test(item) && resultCount.matches(item.getCount()), + firstString, - item -> first.value().test(item) && firstCount.matches(item.getCount()), + first == null ? null : item -> first.value().test(item) && firstCount.matches(item.getCount()), secondString, - second == null || secondCount == null ? null : item -> second.value().test(item) && secondCount.matches(item.getCount()), - - resultString, - item -> result.value().test(item) && resultCount.matches(item.getCount()) + second == null ? null : item -> second.value().test(item) && secondCount.matches(item.getCount()) )); ctx.sendFeedback(Component.translatable("commands.cvillager.goalAdded")); return Command.SINGLE_SUCCESS; } + private static void checkStackSize(WithStringArgument.Result itemPredicate, MinMaxBounds.Ints count) throws CommandSyntaxException { + int maxCount = itemPredicate.value().getPossibleItems().stream().mapToInt(Item::getDefaultMaxStackSize).max().orElse(Item.DEFAULT_MAX_STACK_SIZE); + if (count.min().isPresent() && count.min().get() > maxCount) { + throw ITEM_OVERSTACKED_EXCEPTION.create(itemPredicate.string(), maxCount); + } + if (count.max().isPresent() && count.max().get() > maxCount) { + throw ITEM_OVERSTACKED_EXCEPTION.create(itemPredicate.string(), maxCount); + } + } + private static int listGoals(FabricClientCommandSource source) { if (VillagerCracker.goals.isEmpty()) { source.sendFeedback(Component.translatable("commands.cvillager.listGoals.noGoals").withStyle(style -> style.withColor(ChatFormatting.RED))); diff --git a/src/main/java/net/earthcomputer/clientcommands/command/arguments/ItemAndEnchantmentsPredicateArgument.java b/src/main/java/net/earthcomputer/clientcommands/command/arguments/ItemAndEnchantmentsPredicateArgument.java index 98f9ca55a..d9a13dc69 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/arguments/ItemAndEnchantmentsPredicateArgument.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/arguments/ItemAndEnchantmentsPredicateArgument.java @@ -52,6 +52,8 @@ public class ItemAndEnchantmentsPredicateArgument implements ArgumentType itemPredicate = item -> true; private BiPredicate> enchantmentPredicate = (item, ench) -> true; private boolean constrainMaxLevel = false; + @Nullable + private String suffix; private ItemAndEnchantmentsPredicateArgument(HolderLookup.Provider holderLookupProvider) { this.enchantmentLookup = holderLookupProvider.lookupOrThrow(Registries.ENCHANTMENT); @@ -76,6 +78,11 @@ public ItemAndEnchantmentsPredicateArgument constrainMaxLevel() { return this; } + public ItemAndEnchantmentsPredicateArgument withSuffix(String suffix) { + this.suffix = suffix; + return this; + } + public static ItemAndEnchantmentsPredicate getItemAndEnchantmentsPredicate(CommandContext context, String name) { return context.getArgument(name, ItemAndEnchantmentsPredicate.class); } @@ -222,6 +229,10 @@ private boolean parseEnchantmentInstancePredicate() throws CommandSyntaxExceptio return false; } + if (option == Option.SUFFIX) { + return false; + } + boolean suggest = reader.canRead(); if (option == Option.EXACT) { exact = true; @@ -258,6 +269,7 @@ private enum Option { WITHOUT, EXACT, ORDERED, + SUFFIX, } @Nullable @@ -269,7 +281,7 @@ private Option parseOption() { case "without" -> Option.WITHOUT; case "exactly" -> exact ? null : Option.EXACT; case "ordered" -> ordered || MultiVersionCompat.INSTANCE.getProtocolVersion() >= MultiVersionCompat.V1_21 ? null : Option.ORDERED; - default -> null; + default -> option.equals(suffix) ? Option.SUFFIX : null; }; } @@ -453,6 +465,9 @@ private void suggestOption() { if (!ordered && MultiVersionCompat.INSTANCE.getProtocolVersion() < MultiVersionCompat.V1_21) { validOptions.add("ordered"); } + if (suffix != null) { + validOptions.add(suffix); + } SharedSuggestionProvider.suggest(validOptions, builder); suggestions.add(builder); }; diff --git a/src/main/java/net/earthcomputer/clientcommands/command/arguments/WithStringArgument.java b/src/main/java/net/earthcomputer/clientcommands/command/arguments/WithStringArgument.java index 614decfb0..23a36643e 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/arguments/WithStringArgument.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/arguments/WithStringArgument.java @@ -6,10 +6,10 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import org.apache.commons.lang3.tuple.Pair; import java.util.Collection; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; public class WithStringArgument implements ArgumentType> { @@ -46,5 +46,9 @@ public Collection getExamples() { return delegate.getExamples(); } - public record Result(String string, T value) {} + public record Result(String string, T value) { + public Result map(Function mapper) { + return new Result<>(string, mapper.apply(value)); + } + } } diff --git a/src/main/java/net/earthcomputer/clientcommands/features/VillagerCracker.java b/src/main/java/net/earthcomputer/clientcommands/features/VillagerCracker.java index 5fb0f868d..79cb833db 100644 --- a/src/main/java/net/earthcomputer/clientcommands/features/VillagerCracker.java +++ b/src/main/java/net/earthcomputer/clientcommands/features/VillagerCracker.java @@ -324,16 +324,25 @@ public static void stopRunning() { targetOffer = null; } - public record Goal(String firstString, Predicate first, @Nullable String secondString, @Nullable Predicate second, String resultString, Predicate result) { + public record Goal( + String resultString, + Predicate result, + @Nullable String firstString, + @Nullable Predicate first, + @Nullable String secondString, + @Nullable Predicate second + ) { public boolean matches(Offer offer) { - return first.test(offer.first) - && ((second == null && offer.second == null) || offer.second != null && second != null && second.test(offer.second)) - && result.test(offer.result); + return result.test(offer.result) + && (first == null || first.test(offer.first)) + && (second == null || (offer.second != null && second.test(offer.second))); } @Override public String toString() { - if (secondString == null) { + if (firstString == null) { + return resultString; + } else if (secondString == null) { return String.format("%s = %s", firstString, resultString); } else { return String.format("%s + %s = %s", firstString, secondString, resultString); diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index f9de2c32c..f275bca14 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -258,7 +258,6 @@ "commands.cvillager.help.tooFast": "Help: villager RNG manipulation requires a 20 Hz clock, please be advised that your clock seems to be running faster", "commands.cvillager.help.tooSlow": "Help: villager RNG manipulation requires a 20 Hz clock, please be advised that your clock seems to be running slower", "commands.cvillager.inSync": "Your villager's RNG is cracked", - "commands.cvillager.itemCountOutOfRange": "The specified item count range (%s) overlaps into an item count outside the maximum (%s)", "commands.cvillager.listGoals.noGoals": "There are no villager goals", "commands.cvillager.listGoals.success": "There are %s villager goals:", "commands.cvillager.noCrackedVillagerPresent": "There was no cracked villager available to use",