Skip to content

Commit ce13c0b

Browse files
committed
Allow level ranges in cenchant, add ordered option. Closes #374, closes #328
1 parent f4f20c1 commit ce13c0b

File tree

7 files changed

+107
-60
lines changed

7 files changed

+107
-60
lines changed

src/main/java/net/earthcomputer/clientcommands/command/arguments/ItemAndEnchantmentsPredicateArgument.java

Lines changed: 107 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.mojang.brigadier.suggestion.Suggestions;
1111
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
1212
import net.earthcomputer.clientcommands.util.MultiVersionCompat;
13+
import net.minecraft.advancements.critereon.MinMaxBounds;
1314
import net.minecraft.commands.SharedSuggestionProvider;
1415
import net.minecraft.commands.arguments.ResourceArgument;
1516
import net.minecraft.core.component.DataComponents;
@@ -23,10 +24,12 @@
2324
import net.minecraft.world.item.enchantment.Enchantment;
2425
import net.minecraft.world.item.enchantment.EnchantmentInstance;
2526
import net.minecraft.world.item.enchantment.ItemEnchantments;
27+
import org.jetbrains.annotations.Nullable;
2628

2729
import java.util.ArrayList;
2830
import java.util.Arrays;
2931
import java.util.Collection;
32+
import java.util.Collections;
3033
import java.util.List;
3134
import java.util.concurrent.CompletableFuture;
3235
import java.util.function.BiPredicate;
@@ -37,7 +40,6 @@ public class ItemAndEnchantmentsPredicateArgument implements ArgumentType<ItemAn
3740

3841
private static final Collection<String> EXAMPLES = Arrays.asList("stick with sharpness 4 without sweeping *", "minecraft:diamond_sword with sharpness *");
3942

40-
private static final SimpleCommandExceptionType EXPECTED_WITH_WITHOUT_EXACTLY_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.cenchant.expectedWithWithoutExactly"));
4143
private static final SimpleCommandExceptionType INCOMPATIBLE_ENCHANTMENT_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.cenchant.incompatible"));
4244
private static final DynamicCommandExceptionType ID_INVALID_EXCEPTION = new DynamicCommandExceptionType(id -> Component.translatable("argument.item.id.invalid", id));
4345

@@ -80,24 +82,38 @@ public ItemAndEnchantmentsPredicate parse(StringReader reader) throws CommandSyn
8082
if (parser.exact && (parser.with.size() != enchantments.size())) {
8183
return false;
8284
}
83-
for (EnchantmentInstance with : parser.with) {
84-
boolean found = false;
85-
for (EnchantmentInstance ench : enchantments) {
86-
if (with.enchantment == ench.enchantment && (with.level == -1 || with.level == ench.level)) {
87-
found = true;
88-
break;
85+
if (parser.ordered) {
86+
int enchIndex = 0;
87+
for (EnchantmentInstancePredicate with : parser.with) {
88+
while (enchIndex < enchantments.size() && !with.test(enchantments.get(enchIndex))) {
89+
enchIndex++;
8990
}
91+
if (enchIndex >= enchantments.size()) {
92+
return false;
93+
}
94+
// we're matching, increment index
95+
enchIndex++;
9096
}
91-
if (!found) {
92-
return false;
97+
} else {
98+
for (EnchantmentInstancePredicate with : parser.with) {
99+
boolean found = false;
100+
for (EnchantmentInstance ench : enchantments) {
101+
if (with.test(ench)) {
102+
found = true;
103+
break;
104+
}
105+
}
106+
if (!found) {
107+
return false;
108+
}
93109
}
94110
}
95111
if (parser.exact) {
96112
return true;
97113
}
98-
for (EnchantmentInstance without : parser.without) {
114+
for (EnchantmentInstancePredicate without : parser.without) {
99115
for (EnchantmentInstance ench : enchantments) {
100-
if (without.enchantment == ench.enchantment && (without.level == -1 || without.level == ench.level)) {
116+
if (without.test(ench)) {
101117
return false;
102118
}
103119
}
@@ -149,10 +165,11 @@ private class Parser {
149165
private Consumer<SuggestionsBuilder> suggestor;
150166

151167
private Item item;
152-
private final List<EnchantmentInstance> with = new ArrayList<>();
153-
private final List<EnchantmentInstance> without = new ArrayList<>();
168+
private final List<EnchantmentInstancePredicate> with = new ArrayList<>();
169+
private final List<EnchantmentInstancePredicate> without = new ArrayList<>();
154170

155171
private boolean exact = false;
172+
private boolean ordered = false;
156173

157174
public Parser(StringReader reader) {
158175
this.reader = reader;
@@ -163,9 +180,8 @@ public void parse() throws CommandSyntaxException {
163180

164181
while (reader.canRead()) {
165182
parseSpace();
166-
parseInfoEnchantment();
167-
if (exact) {
168-
return;
183+
if (!parseEnchantmentInstancePredicate()) {
184+
break;
169185
}
170186
}
171187
}
@@ -188,46 +204,64 @@ private Item parseItem() throws CommandSyntaxException {
188204
return item;
189205
}
190206

191-
private void parseInfoEnchantment() throws CommandSyntaxException {
207+
private boolean parseEnchantmentInstancePredicate() throws CommandSyntaxException {
192208
ItemStack stack = new ItemStack(item);
193209

194-
Option option = parseWithWithoutExactly();
210+
int start = reader.getCursor();
211+
Option option = parseOption();
212+
if (option == null) {
213+
reader.setCursor(start);
214+
return false;
215+
}
216+
195217
boolean suggest = reader.canRead();
196-
parseSpace();
197218
if (option == Option.EXACT) {
198219
exact = true;
199-
return;
220+
return true;
221+
}
222+
if (option == Option.ORDERED) {
223+
ordered = true;
224+
return true;
200225
}
226+
227+
if (exact || ordered) {
228+
reader.setCursor(start);
229+
return false;
230+
}
231+
232+
parseSpace();
233+
201234
Enchantment enchantment = parseEnchantment(suggest, option, stack);
202235
suggest = reader.canRead();
203236
parseSpace();
204-
int level = parseEnchantmentLevel(suggest, option, stack, enchantment);
237+
MinMaxBounds.Ints level = parseEnchantmentLevel(suggest, option, stack, enchantment);
205238

206239
if (option == Option.WITH) {
207-
with.add(new EnchantmentInstance(enchantment, level));
240+
with.add(new EnchantmentInstancePredicate(enchantment, level));
208241
} else {
209-
without.add(new EnchantmentInstance(enchantment, level));
242+
without.add(new EnchantmentInstancePredicate(enchantment, level));
210243
}
244+
245+
return true;
211246
}
212247

213248
private enum Option {
214249
WITH,
215250
WITHOUT,
216-
EXACT
251+
EXACT,
252+
ORDERED,
217253
}
218254

219-
private Option parseWithWithoutExactly() throws CommandSyntaxException {
220-
suggestWithWithoutExactly();
221-
int start = reader.getCursor();
255+
@Nullable
256+
private Option parseOption() {
257+
suggestOption();
222258
String option = reader.readUnquotedString();
223259
return switch (option) {
224260
case "with" -> Option.WITH;
225261
case "without" -> Option.WITHOUT;
226-
case "exactly" -> Option.EXACT;
227-
default -> {
228-
reader.setCursor(start);
229-
throw EXPECTED_WITH_WITHOUT_EXACTLY_EXCEPTION.createWithContext(reader);
230-
}
262+
case "exactly" -> exact ? null : Option.EXACT;
263+
case "ordered" -> ordered ? null : Option.ORDERED;
264+
default -> null;
231265
};
232266
}
233267

@@ -239,24 +273,24 @@ private Enchantment parseEnchantment(boolean suggest, Option option, ItemStack s
239273
continue;
240274
}
241275
if (option == Option.WITH) {
242-
for (EnchantmentInstance ench2 : with) {
276+
for (EnchantmentInstancePredicate ench2 : with) {
243277
if (ench2.enchantment == ench || !ench2.enchantment.isCompatibleWith(ench)) {
244278
continue nextEnchantment;
245279
}
246280
}
247-
for (EnchantmentInstance ench2 : without) {
248-
if (ench2.enchantment == ench && ench2.level == -1) {
281+
for (EnchantmentInstancePredicate ench2 : without) {
282+
if (ench2.enchantment == ench && ench2.level.isAny()) {
249283
continue nextEnchantment;
250284
}
251285
}
252286
} else {
253-
for (EnchantmentInstance ench2 : with) {
254-
if (ench2.enchantment == ench && ench2.level == -1) {
287+
for (EnchantmentInstancePredicate ench2 : with) {
288+
if (ench2.enchantment == ench && ench2.level.isAny()) {
255289
continue nextEnchantment;
256290
}
257291
}
258-
for (EnchantmentInstance ench2 : without) {
259-
if (ench2.enchantment == ench && ench2.level == -1) {
292+
for (EnchantmentInstancePredicate ench2 : without) {
293+
if (ench2.enchantment == ench && ench2.level.isAny()) {
260294
continue nextEnchantment;
261295
}
262296
}
@@ -287,7 +321,7 @@ private Enchantment parseEnchantment(boolean suggest, Option option, ItemStack s
287321
return enchantment;
288322
}
289323

290-
private int parseEnchantmentLevel(boolean suggest, Option option, ItemStack stack, Enchantment enchantment) throws CommandSyntaxException {
324+
private MinMaxBounds.Ints parseEnchantmentLevel(boolean suggest, Option option, ItemStack stack, Enchantment enchantment) throws CommandSyntaxException {
291325
int maxLevel;
292326
if (constrainMaxLevel) {
293327
int enchantability = stack.getItem().getEnchantmentValue();
@@ -309,19 +343,19 @@ private int parseEnchantmentLevel(boolean suggest, Option option, ItemStack stac
309343
}
310344

311345
if (option == Option.WITH) {
312-
for (EnchantmentInstance ench : without) {
313-
if (ench.enchantment == enchantment && (level == -1 || level == ench.level)) {
346+
for (EnchantmentInstancePredicate ench : without) {
347+
if (ench.enchantment == enchantment && (level == -1 || ench.level.matches(level))) {
314348
continue nextLevel;
315349
}
316350
}
317351
} else {
318-
for (EnchantmentInstance ench : with) {
319-
if (ench.enchantment == enchantment && (level == -1 || level == ench.level)) {
352+
for (EnchantmentInstancePredicate ench : with) {
353+
if (ench.enchantment == enchantment && (level == -1 || ench.level.matches(level))) {
320354
continue nextLevel;
321355
}
322356
}
323-
for (EnchantmentInstance ench : without) {
324-
if (ench.enchantment == enchantment && (level == -1 || level == ench.level)) {
357+
for (EnchantmentInstancePredicate ench : without) {
358+
if (ench.enchantment == enchantment && (level == -1 || ench.level.matches(level))) {
325359
continue nextLevel;
326360
}
327361
}
@@ -352,15 +386,17 @@ private int parseEnchantmentLevel(boolean suggest, Option option, ItemStack stac
352386

353387
if (reader.peek() == '*') {
354388
reader.skip();
355-
return -1;
389+
return MinMaxBounds.Ints.ANY;
356390
}
357391

358-
int level = reader.readInt();
359-
if (level == -1 || !allowedLevels.contains(level)) {
360-
reader.setCursor(start);
361-
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidInt().createWithContext(reader, level);
392+
int levelStart = reader.getCursor();
393+
MinMaxBounds.Ints result = MinMaxBounds.Ints.fromReader(reader);
394+
if (allowedLevels.stream().noneMatch(result::matches)) {
395+
int levelEnd = reader.getCursor();
396+
reader.setCursor(levelStart);
397+
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidInt().createWithContext(reader, reader.getString().substring(levelStart, levelEnd));
362398
}
363-
return level;
399+
return result;
364400
}
365401

366402
private void parseSpace() throws CommandSyntaxException {
@@ -393,13 +429,30 @@ private void suggestEnchantableItem() {
393429
};
394430
}
395431

396-
private void suggestWithWithoutExactly() {
432+
private void suggestOption() {
397433
int start = reader.getCursor();
398434
suggestor = suggestions -> {
399435
SuggestionsBuilder builder = suggestions.createOffset(start);
400-
SharedSuggestionProvider.suggest(new String[]{"with", "without", "exactly"}, builder);
436+
List<String> validOptions = new ArrayList<>(4);
437+
if (!exact && !ordered) {
438+
Collections.addAll(validOptions, "with", "without");
439+
}
440+
if (!exact) {
441+
validOptions.add("exactly");
442+
}
443+
if (!ordered) {
444+
validOptions.add("ordered");
445+
}
446+
SharedSuggestionProvider.suggest(validOptions, builder);
401447
suggestions.add(builder);
402448
};
403449
}
404450
}
451+
452+
private record EnchantmentInstancePredicate(Enchantment enchantment, MinMaxBounds.Ints level) implements Predicate<EnchantmentInstance> {
453+
@Override
454+
public boolean test(EnchantmentInstance enchInstance) {
455+
return enchantment == enchInstance.enchantment && level.matches(enchInstance.level);
456+
}
457+
}
405458
}

src/main/resources/assets/clientcommands/lang/de_de.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
"commands.ccreativetab.saveFile.failed": "Konnte Kreativtab-Datei nicht speichern",
5858
"commands.ccreativetab.setStack.success": "Stapel in Kreativtab \"%s\" an Position %d erfolgreich auf %s geändert",
5959

60-
"commands.cenchant.expectedWithWithoutExactly": "Erwartet \"with\"/\"without\"/\"exactly\"",
6160
"commands.cenchant.failed": "Es ist unmöglich oder würde zu lange brauchen, diese Verzauberungen zu bekommen",
6261
"commands.cenchant.help.uncrackedPlayerSeed": "Hilfe: du hast den Spielerseed nicht vollständig geknackt",
6362
"commands.cenchant.incompatible": "Unvereinbare Verzauberungen",

src/main/resources/assets/clientcommands/lang/en_us.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
"commands.ccreativetab.saveFile.failed": "Could not save creative tabs file",
5959
"commands.ccreativetab.setStack.success": "Successfully set the stack from \"%s\" at index %d to %s",
6060

61-
"commands.cenchant.expectedWithWithoutExactly": "Expected \"with\"/\"without\"/\"exactly\"",
6261
"commands.cenchant.failed": "It's impossible or would take too long to get those enchantments",
6362
"commands.cenchant.help.uncrackedPlayerSeed": "Help: you have not fully cracked the player seed",
6463
"commands.cenchant.incompatible": "Incompatible enchantments",

src/main/resources/assets/clientcommands/lang/id_id.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
"commands.ccreativetab.saveFile.failed": "Tidak bisa menyimpan file kelompok benda",
5858
"commands.ccreativetab.setStack.success": "Berhasil setel tumpukan dari \"%s\" di indeks %d",
5959

60-
"commands.cenchant.expectedWithWithoutExactly": "Diharapkan \"with (dengan)\"/\"without (tanpa)\"/\"exactly (tepat)\"",
6160
"commands.cenchant.failed": "Tidak mungkin atau akan memakan waktu yang lama untuk mendapatkan pesona tersebut",
6261
"commands.cenchant.help.uncrackedPlayerSeed": "Bantuan: anda belum sepenuhnya memecahkan benih pemain",
6362
"commands.cenchant.incompatible": "Pesona tidak kompatibel",

src/main/resources/assets/clientcommands/lang/ja_jp.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
"commands.ccreativetab.saveFile.failed": "アイテムグループファイルを保存できませんでした",
5858
"commands.ccreativetab.setStack.success": "\"%s\"からのスタックを索引%dに%sを保存するのに成功しました",
5959

60-
"commands.cenchant.expectedWithWithoutExactly": "\"with\"/\"without\"/\"exactly\"が予期されました",
6160
"commands.cenchant.failed": "それらのエンチャントを取得するのは不可能であるか、時間がかかりすぎます",
6261
"commands.cenchant.help.uncrackedPlayerSeed": "ヘルプ: プレイヤーのシードは完全にクラックされていません。",
6362
"commands.cenchant.incompatible": "競合するエンチャント",

src/main/resources/assets/clientcommands/lang/ru_ru.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
"commands.ccreativetab.saveFile.failed": "Не удалось сохранить файл групп элементов",
5858
"commands.ccreativetab.setStack.success": "Успешно установил стак из \"%s\" с индексом %d в %s",
5959

60-
"commands.cenchant.expectedWithWithoutExactly": "Ожидал \"with\"/\"without\"/\"exactly\"",
6160
"commands.cenchant.failed": "Невозможно или это займет слишком много времени, чтобы получить эти чары",
6261
"commands.cenchant.incompatible": "Несовместимые чары",
6362
"commands.cenchant.needEnchantingPrediction": "Эта команда требует, чтобы предсказание зачарования было включено.",

src/main/resources/assets/clientcommands/lang/zh_cn.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
"commands.ccreativetab.saveFile.failed": "无法保存创造模式物品栏文件",
5858
"commands.ccreativetab.setStack.success": "成功将“%s”中第%d个物品设为%s",
5959

60-
"commands.cenchant.expectedWithWithoutExactly": "有效参数:“with”“without”“exactly”",
6160
"commands.cenchant.failed": "你请求的附魔无法获得或者需要太长时间",
6261
"commands.cenchant.help.uncrackedPlayerSeed": "提示:你还没有完全破解玩家种子",
6362
"commands.cenchant.incompatible": "互斥的附魔",

0 commit comments

Comments
 (0)