10
10
import com .mojang .brigadier .suggestion .Suggestions ;
11
11
import com .mojang .brigadier .suggestion .SuggestionsBuilder ;
12
12
import net .earthcomputer .clientcommands .util .MultiVersionCompat ;
13
+ import net .minecraft .advancements .critereon .MinMaxBounds ;
13
14
import net .minecraft .commands .SharedSuggestionProvider ;
14
15
import net .minecraft .commands .arguments .ResourceArgument ;
15
16
import net .minecraft .core .component .DataComponents ;
23
24
import net .minecraft .world .item .enchantment .Enchantment ;
24
25
import net .minecraft .world .item .enchantment .EnchantmentInstance ;
25
26
import net .minecraft .world .item .enchantment .ItemEnchantments ;
27
+ import org .jetbrains .annotations .Nullable ;
26
28
27
29
import java .util .ArrayList ;
28
30
import java .util .Arrays ;
29
31
import java .util .Collection ;
32
+ import java .util .Collections ;
30
33
import java .util .List ;
31
34
import java .util .concurrent .CompletableFuture ;
32
35
import java .util .function .BiPredicate ;
@@ -37,7 +40,6 @@ public class ItemAndEnchantmentsPredicateArgument implements ArgumentType<ItemAn
37
40
38
41
private static final Collection <String > EXAMPLES = Arrays .asList ("stick with sharpness 4 without sweeping *" , "minecraft:diamond_sword with sharpness *" );
39
42
40
- private static final SimpleCommandExceptionType EXPECTED_WITH_WITHOUT_EXACTLY_EXCEPTION = new SimpleCommandExceptionType (Component .translatable ("commands.cenchant.expectedWithWithoutExactly" ));
41
43
private static final SimpleCommandExceptionType INCOMPATIBLE_ENCHANTMENT_EXCEPTION = new SimpleCommandExceptionType (Component .translatable ("commands.cenchant.incompatible" ));
42
44
private static final DynamicCommandExceptionType ID_INVALID_EXCEPTION = new DynamicCommandExceptionType (id -> Component .translatable ("argument.item.id.invalid" , id ));
43
45
@@ -80,24 +82,38 @@ public ItemAndEnchantmentsPredicate parse(StringReader reader) throws CommandSyn
80
82
if (parser .exact && (parser .with .size () != enchantments .size ())) {
81
83
return false ;
82
84
}
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 ++;
89
90
}
91
+ if (enchIndex >= enchantments .size ()) {
92
+ return false ;
93
+ }
94
+ // we're matching, increment index
95
+ enchIndex ++;
90
96
}
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
+ }
93
109
}
94
110
}
95
111
if (parser .exact ) {
96
112
return true ;
97
113
}
98
- for (EnchantmentInstance without : parser .without ) {
114
+ for (EnchantmentInstancePredicate without : parser .without ) {
99
115
for (EnchantmentInstance ench : enchantments ) {
100
- if (without .enchantment == ench . enchantment && ( without . level == - 1 || without . level == ench . level )) {
116
+ if (without .test ( ench )) {
101
117
return false ;
102
118
}
103
119
}
@@ -149,10 +165,11 @@ private class Parser {
149
165
private Consumer <SuggestionsBuilder > suggestor ;
150
166
151
167
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 <>();
154
170
155
171
private boolean exact = false ;
172
+ private boolean ordered = false ;
156
173
157
174
public Parser (StringReader reader ) {
158
175
this .reader = reader ;
@@ -163,9 +180,8 @@ public void parse() throws CommandSyntaxException {
163
180
164
181
while (reader .canRead ()) {
165
182
parseSpace ();
166
- parseInfoEnchantment ();
167
- if (exact ) {
168
- return ;
183
+ if (!parseEnchantmentInstancePredicate ()) {
184
+ break ;
169
185
}
170
186
}
171
187
}
@@ -188,46 +204,64 @@ private Item parseItem() throws CommandSyntaxException {
188
204
return item ;
189
205
}
190
206
191
- private void parseInfoEnchantment () throws CommandSyntaxException {
207
+ private boolean parseEnchantmentInstancePredicate () throws CommandSyntaxException {
192
208
ItemStack stack = new ItemStack (item );
193
209
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
+
195
217
boolean suggest = reader .canRead ();
196
- parseSpace ();
197
218
if (option == Option .EXACT ) {
198
219
exact = true ;
199
- return ;
220
+ return true ;
221
+ }
222
+ if (option == Option .ORDERED ) {
223
+ ordered = true ;
224
+ return true ;
200
225
}
226
+
227
+ if (exact || ordered ) {
228
+ reader .setCursor (start );
229
+ return false ;
230
+ }
231
+
232
+ parseSpace ();
233
+
201
234
Enchantment enchantment = parseEnchantment (suggest , option , stack );
202
235
suggest = reader .canRead ();
203
236
parseSpace ();
204
- int level = parseEnchantmentLevel (suggest , option , stack , enchantment );
237
+ MinMaxBounds . Ints level = parseEnchantmentLevel (suggest , option , stack , enchantment );
205
238
206
239
if (option == Option .WITH ) {
207
- with .add (new EnchantmentInstance (enchantment , level ));
240
+ with .add (new EnchantmentInstancePredicate (enchantment , level ));
208
241
} else {
209
- without .add (new EnchantmentInstance (enchantment , level ));
242
+ without .add (new EnchantmentInstancePredicate (enchantment , level ));
210
243
}
244
+
245
+ return true ;
211
246
}
212
247
213
248
private enum Option {
214
249
WITH ,
215
250
WITHOUT ,
216
- EXACT
251
+ EXACT ,
252
+ ORDERED ,
217
253
}
218
254
219
- private Option parseWithWithoutExactly () throws CommandSyntaxException {
220
- suggestWithWithoutExactly ();
221
- int start = reader . getCursor ();
255
+ @ Nullable
256
+ private Option parseOption () {
257
+ suggestOption ();
222
258
String option = reader .readUnquotedString ();
223
259
return switch (option ) {
224
260
case "with" -> Option .WITH ;
225
261
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 ;
231
265
};
232
266
}
233
267
@@ -239,24 +273,24 @@ private Enchantment parseEnchantment(boolean suggest, Option option, ItemStack s
239
273
continue ;
240
274
}
241
275
if (option == Option .WITH ) {
242
- for (EnchantmentInstance ench2 : with ) {
276
+ for (EnchantmentInstancePredicate ench2 : with ) {
243
277
if (ench2 .enchantment == ench || !ench2 .enchantment .isCompatibleWith (ench )) {
244
278
continue nextEnchantment ;
245
279
}
246
280
}
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 () ) {
249
283
continue nextEnchantment ;
250
284
}
251
285
}
252
286
} 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 () ) {
255
289
continue nextEnchantment ;
256
290
}
257
291
}
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 () ) {
260
294
continue nextEnchantment ;
261
295
}
262
296
}
@@ -287,7 +321,7 @@ private Enchantment parseEnchantment(boolean suggest, Option option, ItemStack s
287
321
return enchantment ;
288
322
}
289
323
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 {
291
325
int maxLevel ;
292
326
if (constrainMaxLevel ) {
293
327
int enchantability = stack .getItem ().getEnchantmentValue ();
@@ -309,19 +343,19 @@ private int parseEnchantmentLevel(boolean suggest, Option option, ItemStack stac
309
343
}
310
344
311
345
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 ) )) {
314
348
continue nextLevel ;
315
349
}
316
350
}
317
351
} 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 ) )) {
320
354
continue nextLevel ;
321
355
}
322
356
}
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 ) )) {
325
359
continue nextLevel ;
326
360
}
327
361
}
@@ -352,15 +386,17 @@ private int parseEnchantmentLevel(boolean suggest, Option option, ItemStack stac
352
386
353
387
if (reader .peek () == '*' ) {
354
388
reader .skip ();
355
- return - 1 ;
389
+ return MinMaxBounds . Ints . ANY ;
356
390
}
357
391
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 ));
362
398
}
363
- return level ;
399
+ return result ;
364
400
}
365
401
366
402
private void parseSpace () throws CommandSyntaxException {
@@ -393,13 +429,30 @@ private void suggestEnchantableItem() {
393
429
};
394
430
}
395
431
396
- private void suggestWithWithoutExactly () {
432
+ private void suggestOption () {
397
433
int start = reader .getCursor ();
398
434
suggestor = suggestions -> {
399
435
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 );
401
447
suggestions .add (builder );
402
448
};
403
449
}
404
450
}
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
+ }
405
458
}
0 commit comments