55import com .mojang .brigadier .CommandDispatcher ;
66import com .mojang .brigadier .arguments .StringArgumentType ;
77import com .mojang .brigadier .builder .LiteralArgumentBuilder ;
8+ import com .mojang .brigadier .arguments .DoubleArgumentType ;
89import com .mojang .brigadier .context .CommandContext ;
910import com .mojang .brigadier .exceptions .CommandSyntaxException ;
1011import com .mojang .brigadier .exceptions .DynamicCommandExceptionType ;
1415import dev .xpple .seedmapper .command .CustomClientCommandSource ;
1516import dev .xpple .seedmapper .config .Configs ;
1617import dev .xpple .seedmapper .render .esp .EspStyle ;
18+ import dev .xpple .seedmapper .render .RenderManager ;
1719import dev .xpple .seedmapper .SeedMapper ;
1820import net .fabricmc .fabric .api .client .command .v2 .FabricClientCommandSource ;
1921import net .minecraft .commands .SharedSuggestionProvider ;
@@ -43,8 +45,9 @@ private EspConfigCommand() {
4345 private static final DynamicCommandExceptionType INVALID_DOUBLE = new DynamicCommandExceptionType (value -> Component .literal ("Invalid number: " + value ));
4446 private static final SimpleCommandExceptionType INVALID_COLOR = new SimpleCommandExceptionType (Component .literal ("Invalid color. Use hex, e.g. #RRGGBB or #AARRGGBB." ));
4547 private static final DynamicCommandExceptionType UNKNOWN_PROPERTY = new DynamicCommandExceptionType (value -> Component .literal ("Unknown ESP property \" " + value + "\" ." ));
48+ private static final DynamicCommandExceptionType UNKNOWN_TARGET = new DynamicCommandExceptionType (value -> Component .literal ("Unknown ESP target \" " + value + "\" ." ));
4649 private static final List <String > PROPERTY_SUGGESTIONS = Arrays .stream (EspProperty .values ())
47- .map (EspProperty ::primaryName )
50+ .map (EspProperty ::displayName )
4851 .toList ();
4952
5053 public static void register (CommandDispatcher <FabricClientCommandSource > dispatcher ) {
@@ -61,38 +64,54 @@ public static void register(CommandDispatcher<FabricClientCommandSource> dispatc
6164 return ;
6265 }
6366
64- for ( EspTarget target : EspTarget . values ()) {
65- LiteralArgumentBuilder <FabricClientCommandSource > targetNode = literal ( target . command ());
66- targetNode . then ( literal ( "get" )
67- . executes ( ctx -> executeGet ( ctx , target , null ) )
68- . then ( argument ( "property" , StringArgumentType . word ( ))
69- . suggests ( EspConfigCommand :: suggestProperties )
70- . executes ( ctx -> executeGet ( ctx , target , getPropertyArgument ( ctx , "property" )))));
71- targetNode . then ( literal ( "set" )
72- .then (argument ( "pairs" , StringArgumentType . greedyString () )
73- . executes ( ctx -> executeSet ( ctx , target ))));
74- targetNode . then ( literal ( "reset" )
75- . executes ( ctx -> executeReset ( ctx , target )));
76- modRoot . addChild ( targetNode . build ( ));
77- }
67+ // Use a single target argument that accepts any case but suggests TitleCase names
68+ com . mojang . brigadier . builder . RequiredArgumentBuilder <FabricClientCommandSource , String > targetArgNode = argument ( " target" , StringArgumentType . word ())
69+ . suggests ( EspConfigCommand :: suggestTargets );
70+ targetArgNode . then ( literal ( "get" )
71+ . executes ( ctx -> executeGet ( ctx , getTargetArgument ( ctx , "target" ), null ))
72+ . then ( argument ( "property" , StringArgumentType . word () )
73+ . suggests ( EspConfigCommand :: suggestProperties )
74+ . executes ( ctx -> executeGet ( ctx , getTargetArgument ( ctx , "target" ), getPropertyArgument ( ctx , "property" )))));
75+ targetArgNode .then (literal ( "set" )
76+ . then ( argument ( "pairs" , StringArgumentType . greedyString ())
77+ . executes ( ctx -> executeSet ( ctx , getTargetArgument ( ctx , "target" )))));
78+ targetArgNode . then ( literal ( "reset" )
79+ . executes ( ctx -> executeReset ( ctx , getTargetArgument ( ctx , "target" )) ));
80+ modRoot . addChild ( targetArgNode . build ());
7881 }
7982
8083 private static void registerDirectSmConfig (CommandDispatcher <FabricClientCommandSource > dispatcher ) {
8184 LiteralArgumentBuilder <FabricClientCommandSource > smRoot = literal ("sm:config" );
82- for (EspTarget target : EspTarget .values ()) {
83- LiteralArgumentBuilder <FabricClientCommandSource > targetNode = literal (target .command ());
84- targetNode .then (literal ("get" )
85- .executes (ctx -> executeGet (ctx , target , null ))
86- .then (argument ("property" , StringArgumentType .word ())
87- .suggests (EspConfigCommand ::suggestProperties )
88- .executes (ctx -> executeGet (ctx , target , getPropertyArgument (ctx , "property" )))));
89- targetNode .then (literal ("set" )
90- .then (argument ("pairs" , StringArgumentType .greedyString ())
91- .executes (ctx -> executeSet (ctx , target ))));
92- targetNode .then (literal ("reset" )
93- .executes (ctx -> executeReset (ctx , target )));
94- smRoot .then (targetNode );
95- }
85+ // single target argument for sm:config that suggests TitleCase but accepts any case when typed
86+ com .mojang .brigadier .builder .RequiredArgumentBuilder <FabricClientCommandSource , String > targetArgNode = argument ("target" , StringArgumentType .word ())
87+ .suggests (EspConfigCommand ::suggestTargets );
88+ targetArgNode .then (literal ("get" )
89+ .executes (ctx -> executeGet (ctx , getTargetArgument (ctx , "target" ), null ))
90+ .then (argument ("property" , StringArgumentType .word ())
91+ .suggests (EspConfigCommand ::suggestProperties )
92+ .executes (ctx -> executeGet (ctx , getTargetArgument (ctx , "target" ), getPropertyArgument (ctx , "property" )))));
93+ targetArgNode .then (literal ("set" )
94+ .then (argument ("pairs" , StringArgumentType .greedyString ())
95+ .executes (ctx -> executeSet (ctx , getTargetArgument (ctx , "target" )))));
96+ targetArgNode .then (literal ("reset" )
97+ .executes (ctx -> executeReset (ctx , getTargetArgument (ctx , "target" ))));
98+ smRoot .then (targetArgNode );
99+ // esptimeout top-level alias
100+ smRoot .then (literal ("esptimeout" )
101+ .executes (ctx -> {
102+ CustomClientCommandSource source = CustomClientCommandSource .of (ctx .getSource ());
103+ source .sendFeedback (Component .literal ("EspTimeoutMinutes = " + Configs .EspTimeoutMinutes ));
104+ return 1 ;
105+ })
106+ .then (argument ("minutes" , DoubleArgumentType .doubleArg (0.0 ))
107+ .executes (ctx -> {
108+ double minutes = DoubleArgumentType .getDouble (ctx , "minutes" );
109+ Configs .EspTimeoutMinutes = minutes ;
110+ Configs .save ();
111+ RenderManager .setHighlightTimeout (Configs .EspTimeoutMinutes );
112+ CustomClientCommandSource .of (ctx .getSource ()).sendFeedback (Component .literal ("Updated EspTimeoutMinutes = " + minutes ));
113+ return 1 ;
114+ })));
96115 dispatcher .register (smRoot );
97116 }
98117
@@ -169,8 +188,25 @@ private static EspProperty getProperty(String raw) throws CommandSyntaxException
169188 return property ;
170189 }
171190
191+ private static CompletableFuture <Suggestions > suggestTargets (CommandContext <FabricClientCommandSource > context , com .mojang .brigadier .suggestion .SuggestionsBuilder builder ) {
192+ return SharedSuggestionProvider .suggest (Arrays .stream (EspTarget .values ()).map (EspTarget ::displayName ).toList (), builder );
193+ }
194+
195+ private static EspTarget getTarget (String raw ) throws CommandSyntaxException {
196+ String normalized = normalizeKey (raw );
197+ EspTarget target = EspTarget .BY_NAME .get (normalized );
198+ if (target == null ) {
199+ throw UNKNOWN_TARGET .create (raw );
200+ }
201+ return target ;
202+ }
203+
204+ private static EspTarget getTargetArgument (CommandContext <FabricClientCommandSource > ctx , String name ) throws CommandSyntaxException {
205+ return getTarget (StringArgumentType .getString (ctx , name ));
206+ }
207+
172208 private static String formatPropertyLine (EspTarget target , EspProperty property , EspStyle style ) {
173- return target .command () + "." + property .displayName () + " = " + property .get (style );
209+ return target .displayName () + "." + property .displayName () + " = " + property .get (style );
174210 }
175211
176212 private static void copyStyle (EspStyle source , EspStyle target ) {
@@ -253,58 +289,59 @@ private record PropertyValue(EspProperty property, String value) {
253289 }
254290
255291 private enum EspTarget {
256- BLOCK ("blockhighlightesp" , EspStyle ::useCommandColorDefaults ) {
292+ BLOCK ("blockhighlightesp" , EspStyle ::useCommandColorDefaults , "BlockHighlightESP" ) {
257293 @ Override
258- public EspStyle style () {
259- return Configs .BlockHighlightESP ;
260- }
294+ public EspStyle style () { return Configs .BlockHighlightESP ; }
261295 },
262- ORE_VEIN ("oreveinesp" , EspStyle ::useCommandColorDefaults ) {
296+ ORE_VEIN ("oreveinesp" , EspStyle ::useCommandColorDefaults , "OreVeinESP" ) {
263297 @ Override
264- public EspStyle style () {
265- return Configs .OreVeinESP ;
266- }
298+ public EspStyle style () { return Configs .OreVeinESP ; }
267299 },
268- TERRAIN ("terrainesp" , EspStyle ::useCommandColorDefaults ) {
300+ TERRAIN ("terrainesp" , EspStyle ::useCommandColorDefaults , "TerrainESP" ) {
269301 @ Override
270- public EspStyle style () {
271- return Configs .TerrainESP ;
272- }
302+ public EspStyle style () { return Configs .TerrainESP ; }
273303 },
274- CANYON ("canyonesp" , EspStyle ::useCommandColorDefaults ) {
304+ CANYON ("canyonesp" , EspStyle ::useCommandColorDefaults , "CanyonESP" ) {
275305 @ Override
276- public EspStyle style () {
277- return Configs .CanyonESP ;
278- }
306+ public EspStyle style () { return Configs .CanyonESP ; }
279307 },
280- CAVE ("caveesp" , EspStyle ::useCommandColorDefaults ) {
308+ CAVE ("caveesp" , EspStyle ::useCommandColorDefaults , "CaveESP" ) {
281309 @ Override
282- public EspStyle style () {
283- return Configs .CaveESP ;
284- }
310+ public EspStyle style () { return Configs .CaveESP ; }
285311 };
286312
287313 private final String command ;
288314 private final Supplier <EspStyle > defaultSupplier ;
315+ private final String displayName ;
289316
290- EspTarget (String command , Supplier <EspStyle > defaultSupplier ) {
317+ EspTarget (String command , Supplier <EspStyle > defaultSupplier , String displayName ) {
291318 this .command = command ;
292319 this .defaultSupplier = defaultSupplier ;
320+ this .displayName = displayName ;
293321 }
294322
295- public String command () {
296- return this . command ;
297- }
323+ public String command () { return this . command ; }
324+
325+ public String displayName () { return this . displayName ; }
298326
299327 public abstract EspStyle style ();
300328
301- public EspStyle defaults () {
302- return this .defaultSupplier .get ();
329+ public EspStyle defaults () { return this .defaultSupplier .get (); }
330+
331+ private static final Map <String , EspTarget > BY_NAME = buildLookup ();
332+
333+ private static Map <String , EspTarget > buildLookup () {
334+ java .util .Map <String , EspTarget > map = new java .util .HashMap <>();
335+ for (EspTarget t : EspTarget .values ()) {
336+ map .putIfAbsent (normalizeKey (t .command ()), t );
337+ map .putIfAbsent (normalizeKey (t .displayName ()), t );
338+ }
339+ return ImmutableMap .copyOf (map );
303340 }
304341 }
305342
306343 private enum EspProperty {
307- OUTLINE_COLOR ("outlinecolor" , "outline" ) {
344+ OUTLINE_COLOR ("outlinecolor" , "OutlineColor" , " outline" ) {
308345 @ Override
309346 void apply (EspStyle style , String value ) throws CommandSyntaxException {
310347 style .OutlineColor = normalizeColorOutput (parseColor (value ));
@@ -316,7 +353,7 @@ String get(EspStyle style) {
316353 return style .OutlineColor ;
317354 }
318355 },
319- OUTLINE_ALPHA ("outlinealpha" ) {
356+ OUTLINE_ALPHA ("outlinealpha" , "OutlineAlpha" ) {
320357 @ Override
321358 void apply (EspStyle style , String value ) throws CommandSyntaxException {
322359 style .OutlineAlpha = parseDouble (value , 0.0D , 1.0D );
@@ -327,7 +364,7 @@ String get(EspStyle style) {
327364 return Double .toString (style .OutlineAlpha );
328365 }
329366 },
330- USE_COMMAND_COLOR ("usecommandcolor" , "usecommandcolour" , "commandcolor" ) {
367+ USE_COMMAND_COLOR ("usecommandcolor" , "UseCommandColor" , " usecommandcolour" , "commandcolor" ) {
331368 @ Override
332369 void apply (EspStyle style , String value ) throws CommandSyntaxException {
333370 style .UseCommandColor = parseBoolean (value );
@@ -338,7 +375,7 @@ String get(EspStyle style) {
338375 return Boolean .toString (style .UseCommandColor );
339376 }
340377 },
341- FILL_ENABLED ("fillenabled" , "fill" ) {
378+ FILL_ENABLED ("fillenabled" , "FillEnabled" , " fill" ) {
342379 @ Override
343380 void apply (EspStyle style , String value ) throws CommandSyntaxException {
344381 style .FillEnabled = parseBoolean (value );
@@ -349,7 +386,7 @@ String get(EspStyle style) {
349386 return Boolean .toString (style .FillEnabled );
350387 }
351388 },
352- FILL_COLOR ("fillcolor" ) {
389+ FILL_COLOR ("fillcolor" , "FillColor" ) {
353390 @ Override
354391 void apply (EspStyle style , String value ) throws CommandSyntaxException {
355392 style .FillColor = normalizeColorOutput (parseColor (value ));
@@ -360,7 +397,7 @@ String get(EspStyle style) {
360397 return style .FillColor ;
361398 }
362399 },
363- FILL_ALPHA ("fillalpha" ) {
400+ FILL_ALPHA ("fillalpha" , "FillAlpha" ) {
364401 @ Override
365402 void apply (EspStyle style , String value ) throws CommandSyntaxException {
366403 style .FillAlpha = parseDouble (value , 0.0D , 1.0D );
@@ -371,7 +408,7 @@ String get(EspStyle style) {
371408 return Double .toString (style .FillAlpha );
372409 }
373410 },
374- RAINBOW ("rainbow" ) {
411+ RAINBOW ("rainbow" , "Rainbow" ) {
375412 @ Override
376413 void apply (EspStyle style , String value ) throws CommandSyntaxException {
377414 style .Rainbow = parseBoolean (value );
@@ -382,7 +419,7 @@ String get(EspStyle style) {
382419 return Boolean .toString (style .Rainbow );
383420 }
384421 },
385- RAINBOW_SPEED ("rainbowspeed" ) {
422+ RAINBOW_SPEED ("rainbowspeed" , "RainbowSpeed" ) {
386423 @ Override
387424 void apply (EspStyle style , String value ) throws CommandSyntaxException {
388425 style .RainbowSpeed = parseDouble (value , 0.05D , 5.0D );
@@ -396,36 +433,37 @@ String get(EspStyle style) {
396433
397434 private static final Map <String , EspProperty > BY_NAME = buildLookup ();
398435 private final String primaryName ;
436+ private final String displayName ;
399437 private final List <String > aliases ;
400438
401- EspProperty (String primaryName , String ... aliases ) {
439+ EspProperty (String primaryName , String displayName , String ... aliases ) {
402440 this .primaryName = primaryName ;
441+ this .displayName = displayName ;
403442 List <String > aliasList = new ArrayList <>(aliases .length + 1 );
404443 aliasList .add (primaryName );
405444 aliasList .addAll (Arrays .asList (aliases ));
406445 this .aliases = Collections .unmodifiableList (aliasList );
407446 }
408447
409- public String primaryName () {
410- return this .primaryName ;
411- }
448+ public String primaryName () { return this .primaryName ; }
412449
413- public String displayName () {
414- return this .primaryName ;
415- }
450+ public String displayName () { return this .displayName ; }
416451
417452 abstract void apply (EspStyle style , String value ) throws CommandSyntaxException ;
418453
419454 abstract String get (EspStyle style );
420455
421456 private static Map <String , EspProperty > buildLookup () {
422- ImmutableMap .Builder <String , EspProperty > builder = ImmutableMap .builder ();
457+ // Build into a mutable map and avoid duplicate keys (case-insensitive normalized)
458+ java .util .Map <String , EspProperty > map = new java .util .HashMap <>();
423459 for (EspProperty property : EspProperty .values ()) {
424460 for (String alias : property .aliases ) {
425- builder . put (normalizeKey (alias ), property );
461+ map . putIfAbsent (normalizeKey (alias ), property );
426462 }
463+ // also accept displayName as a valid input
464+ map .putIfAbsent (normalizeKey (property .displayName ), property );
427465 }
428- return builder . build ( );
466+ return ImmutableMap . copyOf ( map );
429467 }
430468 }
431469}
0 commit comments