From 650d3f0fb4c2d240089613ac3ffe297a1b7e392a Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Sun, 13 Nov 2022 23:03:30 +0100 Subject: [PATCH 1/5] Rewrite FormattedTextArgumentType --- .../clientcommands/command/NoteCommand.java | 4 +- .../arguments/FormattedTextArgumentType.java | 194 +++++------------- 2 files changed, 58 insertions(+), 140 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/NoteCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/NoteCommand.java index 7b63f6edf..dca2eb484 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/NoteCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/NoteCommand.java @@ -3,7 +3,7 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.minecraft.text.MutableText; +import net.minecraft.text.Text; import static net.earthcomputer.clientcommands.command.arguments.FormattedTextArgumentType.*; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; @@ -16,7 +16,7 @@ public static void register(CommandDispatcher dispatc .executes(ctx -> note(ctx.getSource(), getFormattedText(ctx, "message"))))); } - private static int note(FabricClientCommandSource source, MutableText message) { + private static int note(FabricClientCommandSource source, Text message) { source.getClient().inGameHud.getChatHud().addMessage(message); return Command.SINGLE_SUCCESS; } diff --git a/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedTextArgumentType.java b/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedTextArgumentType.java index 816f01207..4fc3475a1 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedTextArgumentType.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedTextArgumentType.java @@ -1,40 +1,43 @@ package net.earthcomputer.clientcommands.command.arguments; -import com.google.common.collect.ImmutableMap; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.command.CommandSource; -import net.minecraft.text.*; +import net.minecraft.text.MutableText; +import net.minecraft.text.Style; +import net.minecraft.text.Text; import net.minecraft.util.Formatting; -import net.minecraft.util.Identifier; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; import java.util.concurrent.CompletableFuture; -import java.util.function.BiFunction; import java.util.function.Consumer; -public class FormattedTextArgumentType implements ArgumentType { +public class FormattedTextArgumentType implements ArgumentType { - private static final Collection EXAMPLES = Arrays.asList("Earth", "bold{xpple}", "bold{italic{red{nwex}}}"); + private static final Collection EXAMPLES = Arrays.asList("Earth", "&lxpple", "&l&o#fb8919nwex"); - private FormattedTextArgumentType() { - } + private static final SimpleCommandExceptionType EXPECTED_FORMATTING_CODE_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.client.expectedFormattingCode")); + private static final SimpleCommandExceptionType UNKNOWN_FORMATTING_CODE_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.client.unknownFormattingCode")); + private static final SimpleCommandExceptionType EXPECTED_HEX_VALUE_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.client.expectedHexValue")); + private static final SimpleCommandExceptionType INVALID_HEX_VALUE_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.client.invalidHexValue")); public static FormattedTextArgumentType formattedText() { return new FormattedTextArgumentType(); } - public static MutableText getFormattedText(CommandContext context, String arg) { - return context.getArgument(arg, MutableText.class); + public static Text getFormattedText(CommandContext context, String arg) { + return context.getArgument(arg, Text.class); } @Override - public MutableText parse(StringReader reader) throws CommandSyntaxException { + public Text parse(StringReader reader) throws CommandSyntaxException { return new Parser(reader).parse(); } @@ -44,10 +47,9 @@ public CompletableFuture listSuggestions(CommandContext cont reader.setCursor(builder.getStart()); Parser parser = new Parser(reader); - try { parser.parse(); - } catch (CommandSyntaxException ignored) { + } catch (CommandSyntaxException ignore) { } if (parser.suggestor != null) { @@ -70,135 +72,51 @@ public Parser(StringReader reader) { this.reader = reader; } - public MutableText parse() throws CommandSyntaxException { - int cursor = reader.getCursor(); - suggestor = builder -> { - SuggestionsBuilder newBuilder = builder.createOffset(cursor); - CommandSource.suggestMatching(FormattedText.FORMATTING.keySet(), newBuilder); - builder.add(newBuilder); - }; - - String word = reader.readUnquotedString(); - - if (FormattedText.FORMATTING.containsKey(word.toLowerCase(Locale.ROOT))) { - FormattedText.Styler styler = FormattedText.FORMATTING.get(word.toLowerCase(Locale.ROOT)); - suggestor = null; - reader.skipWhitespace(); - - if (!reader.canRead() || reader.peek() != '{') { - throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedSymbol().createWithContext(reader, "{"); - } - reader.skip(); - reader.skipWhitespace(); - MutableText literalText; - List arguments = new ArrayList<>(); - if (reader.canRead()) { - if (reader.peek() != '}') { - if (StringReader.isQuotedStringStart(reader.peek())) { - literalText = Text.literal(reader.readQuotedString()); - } else { - literalText = parse(); - } - reader.skipWhitespace(); - while (reader.canRead() && reader.peek() != '}') { - if (arguments.isEmpty()) { - suggestor = builder -> { - SuggestionsBuilder newBuilder = builder.createOffset(cursor); - CommandSource.suggestMatching(styler.suggestions, newBuilder); - builder.add(newBuilder); - }; - } - if (reader.peek() != ',') { - throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedSymbol().createWithContext(reader, ","); - } - reader.skip(); - reader.skipWhitespace(); - arguments.add(readArgument()); - reader.skipWhitespace(); + public Text parse() throws CommandSyntaxException { + MutableText text = Text.empty(); + Style style = Style.EMPTY; + while (reader.canRead()) { + char c = reader.read(); + if (c == '&') { // Formatting.FORMATTING_CODE_PREFIX is not writable in chat + suggestor = suggestions -> { + SuggestionsBuilder builder = suggestions.createOffset(reader.getCursor()); + CommandSource.suggestMatching(Arrays.stream(Formatting.values()).map(f -> String.valueOf(f.getCode())), builder); + suggestions.add(builder); + }; + if (!reader.canRead()) { + throw EXPECTED_FORMATTING_CODE_EXCEPTION.create(); + } + char code = reader.read(); + Formatting formatting = Formatting.byCode(code); + if (formatting == null) { + throw UNKNOWN_FORMATTING_CODE_EXCEPTION.create(); + } + style = style.withFormatting(formatting); + } else if (c == '#') { // TextColor.RGB_PREFIX is private + if (!reader.canRead()) { + throw EXPECTED_HEX_VALUE_EXCEPTION.create(); + } + StringBuilder builder = new StringBuilder(); + int hex = -1; + for (int i = 0; i < 6 && reader.canRead(); i++) { + builder.append(reader.peek()); + try { + hex = Integer.parseInt(builder.toString(), 16); + } catch (NumberFormatException e) { + break; } - } else { - literalText = Text.literal(""); + reader.skip(); + } + if (hex == -1) { + throw INVALID_HEX_VALUE_EXCEPTION.create(); } + style = style.withColor(hex); } else { - throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedSymbol().createWithContext(reader, "}"); - } - reader.skip(); - - if (styler.argumentCount != arguments.size()) { - reader.setCursor(cursor); - reader.readUnquotedString(); - throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); + text.append(Text.literal(String.valueOf(c)).setStyle(style)); } - return new FormattedText(styler.operator, literalText, arguments).style(); - } else { - return Text.literal(word + readArgument()); - } - } - - private String readArgument() { - final int start = reader.getCursor(); - while (reader.canRead() && isAllowedInArgument(reader.peek())) { - reader.skip(); + suggestor = null; } - return reader.getString().substring(start, reader.getCursor()); - } - - private static boolean isAllowedInArgument(final char c) { - return c != ',' && c != '{' && c != '}'; + return text; } } - - static class FormattedText { - private static final Map FORMATTING = ImmutableMap.builder() - .put("aqua", new Styler((s, o) -> s.withFormatting(Formatting.AQUA), 0)) - .put("black", new Styler((s, o) -> s.withFormatting(Formatting.BLACK), 0)) - .put("blue", new Styler((s, o) -> s.withFormatting(Formatting.BLUE), 0)) - .put("bold", new Styler((s, o) -> s.withFormatting(Formatting.BOLD), 0)) - .put("dark_aqua", new Styler((s, o) -> s.withFormatting(Formatting.DARK_AQUA), 0)) - .put("dark_blue", new Styler((s, o) -> s.withFormatting(Formatting.DARK_BLUE), 0)) - .put("dark_gray", new Styler((s, o) -> s.withFormatting(Formatting.DARK_GRAY), 0)) - .put("dark_green", new Styler((s, o) -> s.withFormatting(Formatting.DARK_GREEN), 0)) - .put("dark_purple", new Styler((s, o) -> s.withFormatting(Formatting.DARK_PURPLE), 0)) - .put("dark_red", new Styler((s, o) -> s.withFormatting(Formatting.DARK_RED), 0)) - .put("gold", new Styler((s, o) -> s.withFormatting(Formatting.GOLD), 0)) - .put("gray", new Styler((s, o) -> s.withFormatting(Formatting.GRAY), 0)) - .put("green", new Styler((s, o) -> s.withFormatting(Formatting.GREEN), 0)) - .put("italic", new Styler((s, o) -> s.withFormatting(Formatting.ITALIC), 0)) - .put("light_purple", new Styler((s, o) -> s.withFormatting(Formatting.LIGHT_PURPLE), 0)) - .put("obfuscated", new Styler((s, o) -> s.withFormatting(Formatting.OBFUSCATED), 0)) - .put("red", new Styler((s, o) -> s.withFormatting(Formatting.RED), 0)) - .put("reset", new Styler((s, o) -> s.withFormatting(Formatting.RESET), 0)) - .put("strikethrough", new Styler((s, o) -> s.withFormatting(Formatting.STRIKETHROUGH), 0)) - .put("underline", new Styler((s, o) -> s.withFormatting(Formatting.UNDERLINE), 0)) - .put("white", new Styler((s, o) -> s.withFormatting(Formatting.WHITE), 0)) - .put("yellow", new Styler((s, o) -> s.withFormatting(Formatting.YELLOW), 0)) - - .put("font", new Styler((s, o) -> s.withFont(Identifier.tryParse(o.get(0))), 1, "alt", "default")) - .put("hex", new Styler((s, o) -> s.withColor(TextColor.fromRgb(Integer.parseInt(o.get(0), 16))), 1)) - .put("insert", new Styler((s, o) -> s.withInsertion(o.get(0)), 1)) - - .put("click", new Styler((s, o) -> s.withClickEvent(new ClickEvent(ClickEvent.Action.byName(o.get(0)), o.get(1))), 2, "change_page", "copy_to_clipboard", "open_file", "open_url", "run_command", "suggest_command")) - .put("hover", new Styler((s, o) -> s.withHoverEvent(HoverEvent.Action.byName(o.get(0)).buildHoverEvent(Text.of(o.get(1)))), 2, "show_entity", "show_item", "show_text")) - - // aliases - .put("strike", new Styler((s, o) -> s.withFormatting(Formatting.STRIKETHROUGH), 0)) - .put("magic", new Styler((s, o) -> s.withFormatting(Formatting.OBFUSCATED), 0)) - .build(); - - private final BiFunction, Style> styler; - private final MutableText argument; - private final List optional; - - public FormattedText(BiFunction, Style> styler, MutableText argument, List optional) { - this.styler = styler; - this.argument = argument; - this.optional = optional; - } - - public MutableText style() { - return this.argument.setStyle(this.styler.apply(this.argument.getStyle(), this.optional)); - } - - private record Styler(BiFunction, Style> operator, int argumentCount, String... suggestions) {} - } } From e2954fabbd9107ad862a4cad3eee0868559e2c6a Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Sun, 13 Nov 2022 23:23:03 +0100 Subject: [PATCH 2/5] Add translations --- src/main/resources/assets/clientcommands/lang/en_us.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index 18213013a..238338c5d 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -206,6 +206,10 @@ "commands.client.notClient": "Not a client-side command", "commands.client.tooFewArguments": "Too few arguments", "commands.client.invalidArgumentException": "Invalid argument '%s'", + "commands.client.expectedFormattingCode": "Expected a formatting code", + "commands.client.unknownFormattingCode": "Unknown formatting code", + "commands.client.expectedHexValue": "Expected a hex value", + "commands.client.invalidHexValue": "Invalid hex value", "chorusManip.landing.success": "Landing on: %d, %d, %d", "chorusManip.landing.failed": "Landing manipulation not possible", From 8519a5971746f9c1416650c8621771ac97a107d1 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Wed, 25 Sep 2024 15:18:26 +0200 Subject: [PATCH 3/5] Update FormattedComponentArgumentType and use it for /cwe --- .../clientcommands/c2c/C2CPacketHandler.java | 10 ++- .../clientcommands/command/NoteCommand.java | 6 +- .../command/WhisperEncryptedCommand.java | 13 +-- ...va => FormattedComponentArgumentType.java} | 47 +++++----- ...ment.java => StyledComponentArgument.java} | 90 +++++++++---------- src/main/resources/clientcommands.aw | 1 + 6 files changed, 89 insertions(+), 78 deletions(-) rename src/main/java/net/earthcomputer/clientcommands/command/arguments/{FormattedTextArgumentType.java => FormattedComponentArgumentType.java} (65%) rename src/main/java/net/earthcomputer/clientcommands/command/arguments/{FormattedComponentArgument.java => StyledComponentArgument.java} (68%) diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java b/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java index 270fcfc78..8413c1eb0 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java @@ -1,5 +1,6 @@ package net.earthcomputer.clientcommands.c2c; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; @@ -9,6 +10,7 @@ import net.earthcomputer.clientcommands.c2c.packets.PutTicTacToeMarkC2CPacket; import net.earthcomputer.clientcommands.c2c.packets.StartTicTacToeGameC2CPacket; import net.earthcomputer.clientcommands.command.ListenCommand; +import net.earthcomputer.clientcommands.command.arguments.FormattedComponentArgumentType; import net.earthcomputer.clientcommands.interfaces.IClientPacketListener_C2C; import net.earthcomputer.clientcommands.command.TicTacToeCommand; import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; @@ -188,12 +190,18 @@ public static boolean handleC2CPacket(String content, String sender) { public void onMessageC2CPacket(MessageC2CPacket packet) { String sender = packet.sender(); String message = packet.message(); + MutableComponent formattedComponent; + try { + formattedComponent = FormattedComponentArgumentType.formattedComponent().parse(new StringReader(message)); + } catch (CommandSyntaxException e) { + formattedComponent = Component.literal(message); + } MutableComponent prefix = Component.empty(); prefix.append(Component.literal("[").withStyle(ChatFormatting.DARK_GRAY)); prefix.append(Component.literal("/cwe").withStyle(ChatFormatting.AQUA)); prefix.append(Component.literal("]").withStyle(ChatFormatting.DARK_GRAY)); prefix.append(Component.literal(" ")); - Component component = prefix.append(Component.translatable("c2cpacket.messageC2CPacket.incoming", sender, message).withStyle(ChatFormatting.GRAY)); + Component component = prefix.append(Component.translatable("c2cpacket.messageC2CPacket.incoming", sender, formattedComponent)); Minecraft.getInstance().gui.getChat().addMessage(component); } diff --git a/src/main/java/net/earthcomputer/clientcommands/command/NoteCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/NoteCommand.java index e16475533..3a587c8d7 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/NoteCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/NoteCommand.java @@ -5,15 +5,15 @@ import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.network.chat.MutableComponent; -import static net.earthcomputer.clientcommands.command.arguments.FormattedComponentArgument.*; +import static net.earthcomputer.clientcommands.command.arguments.StyledComponentArgument.*; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; public class NoteCommand { public static void register(CommandDispatcher dispatcher) { dispatcher.register(literal("cnote") - .then(argument("message", formattedComponent()) - .executes(ctx -> note(ctx.getSource(), getFormattedComponent(ctx, "message"))))); + .then(argument("message", styledComponent()) + .executes(ctx -> note(ctx.getSource(), getStyledComponent(ctx, "message"))))); } private static int note(FabricClientCommandSource source, MutableComponent message) { diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java index 0bb5ede87..bbd579c2c 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java @@ -13,8 +13,9 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; -import static com.mojang.brigadier.arguments.StringArgumentType.*; import static dev.xpple.clientarguments.arguments.CGameProfileArgument.*; +import static net.earthcomputer.clientcommands.command.arguments.FormattedComponentArgumentType.*; +import static net.earthcomputer.clientcommands.command.arguments.WithStringArgument.*; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; public class WhisperEncryptedCommand { @@ -24,24 +25,24 @@ public class WhisperEncryptedCommand { public static void register(CommandDispatcher dispatcher) { dispatcher.register(literal("cwe") .then(argument("player", gameProfile(true)) - .then(argument("message", greedyString()) - .executes((ctx) -> whisper(ctx.getSource(), getSingleProfileArgument(ctx, "player"), getString(ctx, "message")))))); + .then(argument("message", withString(formattedComponent())) + .executes((ctx) -> whisper(ctx.getSource(), getSingleProfileArgument(ctx, "player"), getWithString(ctx, "message", MutableComponent.class)))))); } - private static int whisper(FabricClientCommandSource source, GameProfile player, String message) throws CommandSyntaxException { + private static int whisper(FabricClientCommandSource source, GameProfile player, Result result) throws CommandSyntaxException { PlayerInfo recipient = source.getClient().getConnection().getPlayerInfo(player.getId()); if (recipient == null) { throw PLAYER_NOT_FOUND_EXCEPTION.create(); } - MessageC2CPacket packet = new MessageC2CPacket(source.getClient().getConnection().getLocalGameProfile().getName(), message); + MessageC2CPacket packet = new MessageC2CPacket(source.getClient().getConnection().getLocalGameProfile().getName(), result.string()); C2CPacketHandler.getInstance().sendPacket(packet, recipient); MutableComponent prefix = Component.empty(); prefix.append(Component.literal("[").withStyle(ChatFormatting.DARK_GRAY)); prefix.append(Component.literal("/cwe").withStyle(ChatFormatting.AQUA)); prefix.append(Component.literal("]").withStyle(ChatFormatting.DARK_GRAY)); prefix.append(Component.literal(" ")); - Component component = prefix.append(Component.translatable("c2cpacket.messageC2CPacket.outgoing", recipient.getProfile().getName(), message).withStyle(ChatFormatting.GRAY)); + Component component = prefix.append(Component.translatable("c2cpacket.messageC2CPacket.outgoing", recipient.getProfile().getName(), result.value())); source.sendFeedback(component); return Command.SINGLE_SUCCESS; } diff --git a/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedTextArgumentType.java b/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgumentType.java similarity index 65% rename from src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedTextArgumentType.java rename to src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgumentType.java index 4fc3475a1..1a0c21c18 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedTextArgumentType.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgumentType.java @@ -8,36 +8,37 @@ import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.minecraft.command.CommandSource; -import net.minecraft.text.MutableText; -import net.minecraft.text.Style; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; +import net.minecraft.ChatFormatting; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.Style; +import net.minecraft.network.chat.TextColor; import java.util.Arrays; import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -public class FormattedTextArgumentType implements ArgumentType { +public class FormattedComponentArgumentType implements ArgumentType { private static final Collection EXAMPLES = Arrays.asList("Earth", "&lxpple", "&l&o#fb8919nwex"); - private static final SimpleCommandExceptionType EXPECTED_FORMATTING_CODE_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.client.expectedFormattingCode")); - private static final SimpleCommandExceptionType UNKNOWN_FORMATTING_CODE_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.client.unknownFormattingCode")); - private static final SimpleCommandExceptionType EXPECTED_HEX_VALUE_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.client.expectedHexValue")); - private static final SimpleCommandExceptionType INVALID_HEX_VALUE_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.client.invalidHexValue")); + private static final SimpleCommandExceptionType EXPECTED_FORMATTING_CODE_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.client.expectedFormattingCode")); + private static final SimpleCommandExceptionType UNKNOWN_FORMATTING_CODE_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.client.unknownFormattingCode")); + private static final SimpleCommandExceptionType EXPECTED_HEX_VALUE_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.client.expectedHexValue")); + private static final SimpleCommandExceptionType INVALID_HEX_VALUE_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.client.invalidHexValue")); - public static FormattedTextArgumentType formattedText() { - return new FormattedTextArgumentType(); + public static FormattedComponentArgumentType formattedComponent() { + return new FormattedComponentArgumentType(); } - public static Text getFormattedText(CommandContext context, String arg) { - return context.getArgument(arg, Text.class); + public static MutableComponent getFormattedComponent(CommandContext context, String arg) { + return context.getArgument(arg, MutableComponent.class); } @Override - public Text parse(StringReader reader) throws CommandSyntaxException { + public MutableComponent parse(StringReader reader) throws CommandSyntaxException { return new Parser(reader).parse(); } @@ -72,27 +73,27 @@ public Parser(StringReader reader) { this.reader = reader; } - public Text parse() throws CommandSyntaxException { - MutableText text = Text.empty(); + public MutableComponent parse() throws CommandSyntaxException { + MutableComponent text = Component.empty(); Style style = Style.EMPTY; while (reader.canRead()) { char c = reader.read(); - if (c == '&') { // Formatting.FORMATTING_CODE_PREFIX is not writable in chat + if (c == '&') { // ChatFormatting.PREFIX_CODE is not writable in chat suggestor = suggestions -> { SuggestionsBuilder builder = suggestions.createOffset(reader.getCursor()); - CommandSource.suggestMatching(Arrays.stream(Formatting.values()).map(f -> String.valueOf(f.getCode())), builder); + SharedSuggestionProvider.suggest(Arrays.stream(ChatFormatting.values()).map(f -> String.valueOf(f.getChar())), builder); suggestions.add(builder); }; if (!reader.canRead()) { throw EXPECTED_FORMATTING_CODE_EXCEPTION.create(); } char code = reader.read(); - Formatting formatting = Formatting.byCode(code); + ChatFormatting formatting = ChatFormatting.getByCode(code); if (formatting == null) { throw UNKNOWN_FORMATTING_CODE_EXCEPTION.create(); } - style = style.withFormatting(formatting); - } else if (c == '#') { // TextColor.RGB_PREFIX is private + style = style.applyFormat(formatting); + } else if (c == TextColor.CUSTOM_COLOR_PREFIX.charAt(0)) { if (!reader.canRead()) { throw EXPECTED_HEX_VALUE_EXCEPTION.create(); } @@ -112,7 +113,7 @@ public Text parse() throws CommandSyntaxException { } style = style.withColor(hex); } else { - text.append(Text.literal(String.valueOf(c)).setStyle(style)); + text.append(Component.literal(String.valueOf(c)).setStyle(style)); } suggestor = null; } diff --git a/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgument.java b/src/main/java/net/earthcomputer/clientcommands/command/arguments/StyledComponentArgument.java similarity index 68% rename from src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgument.java rename to src/main/java/net/earthcomputer/clientcommands/command/arguments/StyledComponentArgument.java index 280fa8fc9..1d43347f8 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgument.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/arguments/StyledComponentArgument.java @@ -34,20 +34,20 @@ import java.util.function.Consumer; import java.util.function.Function; -public class FormattedComponentArgument implements ArgumentType { +public class StyledComponentArgument implements ArgumentType { private static final Collection EXAMPLES = Arrays.asList("Earth", "bold{xpple}", "bold{italic{red{nwex}}}"); private static final DynamicCommandExceptionType INVALID_CLICK_ACTION = new DynamicCommandExceptionType(action -> Component.translatable("commands.client.invalidClickAction", action)); private static final DynamicCommandExceptionType INVALID_HOVER_ACTION = new DynamicCommandExceptionType(action -> Component.translatable("commands.client.invalidHoverAction", action)); private static final DynamicCommandExceptionType INVALID_HOVER_EVENT = new DynamicCommandExceptionType(event -> Component.translatable("commands.client.invalidHoverEvent", event)); - private FormattedComponentArgument() { + private StyledComponentArgument() { } - public static FormattedComponentArgument formattedComponent() { - return new FormattedComponentArgument(); + public static StyledComponentArgument styledComponent() { + return new StyledComponentArgument(); } - public static MutableComponent getFormattedComponent(CommandContext context, String arg) { + public static MutableComponent getStyledComponent(CommandContext context, String arg) { return context.getArgument(arg, MutableComponent.class); } @@ -92,14 +92,14 @@ public MutableComponent parse() throws CommandSyntaxException { int cursor = reader.getCursor(); suggestor = builder -> { SuggestionsBuilder newBuilder = builder.createOffset(cursor); - SharedSuggestionProvider.suggest(FormattedText.FORMATTING.keySet(), newBuilder); + SharedSuggestionProvider.suggest(StyledComponent.FORMATTING.keySet(), newBuilder); builder.add(newBuilder); }; String word = reader.readUnquotedString(); - if (FormattedText.FORMATTING.containsKey(word.toLowerCase(Locale.ROOT))) { - FormattedText.Styler styler = FormattedText.FORMATTING.get(word.toLowerCase(Locale.ROOT)); + if (StyledComponent.FORMATTING.containsKey(word.toLowerCase(Locale.ROOT))) { + StyledComponent.Styler styler = StyledComponent.FORMATTING.get(word.toLowerCase(Locale.ROOT)); suggestor = null; reader.skipWhitespace(); @@ -147,7 +147,7 @@ public MutableComponent parse() throws CommandSyntaxException { reader.readUnquotedString(); throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); } - return new FormattedText(styler.operator, literalText, arguments).style(); + return new StyledComponent(styler.operator, literalText, arguments).style(); } else { return Component.literal(word + readArgument()); } @@ -166,48 +166,48 @@ private static boolean isAllowedInArgument(final char c) { } } - static class FormattedText { + static class StyledComponent { private static final Map FORMATTING = ImmutableMap.builder() - .put("aqua", new Styler((s, o) -> s.applyFormat(ChatFormatting.AQUA), 0)) - .put("black", new Styler((s, o) -> s.applyFormat(ChatFormatting.BLACK), 0)) - .put("blue", new Styler((s, o) -> s.applyFormat(ChatFormatting.BLUE), 0)) - .put("bold", new Styler((s, o) -> s.applyFormat(ChatFormatting.BOLD), 0)) - .put("dark_aqua", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_AQUA), 0)) - .put("dark_blue", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_BLUE), 0)) - .put("dark_gray", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_GRAY), 0)) - .put("dark_green", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_GREEN), 0)) - .put("dark_purple", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_PURPLE), 0)) - .put("dark_red", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_RED), 0)) - .put("gold", new Styler((s, o) -> s.applyFormat(ChatFormatting.GOLD), 0)) - .put("gray", new Styler((s, o) -> s.applyFormat(ChatFormatting.GRAY), 0)) - .put("green", new Styler((s, o) -> s.applyFormat(ChatFormatting.GREEN), 0)) - .put("italic", new Styler((s, o) -> s.applyFormat(ChatFormatting.ITALIC), 0)) - .put("light_purple", new Styler((s, o) -> s.applyFormat(ChatFormatting.LIGHT_PURPLE), 0)) - .put("obfuscated", new Styler((s, o) -> s.applyFormat(ChatFormatting.OBFUSCATED), 0)) - .put("red", new Styler((s, o) -> s.applyFormat(ChatFormatting.RED), 0)) - .put("reset", new Styler((s, o) -> s.applyFormat(ChatFormatting.RESET), 0)) - .put("strikethrough", new Styler((s, o) -> s.applyFormat(ChatFormatting.STRIKETHROUGH), 0)) - .put("underline", new Styler((s, o) -> s.applyFormat(ChatFormatting.UNDERLINE), 0)) - .put("white", new Styler((s, o) -> s.applyFormat(ChatFormatting.WHITE), 0)) - .put("yellow", new Styler((s, o) -> s.applyFormat(ChatFormatting.YELLOW), 0)) - - .put("font", new Styler((s, o) -> s.withFont(ResourceLocation.tryParse(o.getFirst())), 1, "alt", "default")) - .put("hex", new Styler((s, o) -> s.withColor(TextColor.fromRgb(Integer.parseInt(o.getFirst(), 16))), 1)) - .put("insert", new Styler((s, o) -> s.withInsertion(o.getFirst()), 1)) - - .put("click", new Styler((s, o) -> s.withClickEvent(parseClickEvent(o.getFirst(), o.get(1))), 2, "change_page", "copy_to_clipboard", "open_file", "open_url", "run_command", "suggest_command")) - .put("hover", new Styler((s, o) -> s.withHoverEvent(parseHoverEvent(o.getFirst(), o.get(1))), 2, "show_entity", "show_item", "show_text")) - - // aliases - .put("strike", new Styler((s, o) -> s.applyFormat(ChatFormatting.STRIKETHROUGH), 0)) - .put("magic", new Styler((s, o) -> s.applyFormat(ChatFormatting.OBFUSCATED), 0)) - .build(); + .put("aqua", new Styler((s, o) -> s.applyFormat(ChatFormatting.AQUA), 0)) + .put("black", new Styler((s, o) -> s.applyFormat(ChatFormatting.BLACK), 0)) + .put("blue", new Styler((s, o) -> s.applyFormat(ChatFormatting.BLUE), 0)) + .put("bold", new Styler((s, o) -> s.applyFormat(ChatFormatting.BOLD), 0)) + .put("dark_aqua", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_AQUA), 0)) + .put("dark_blue", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_BLUE), 0)) + .put("dark_gray", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_GRAY), 0)) + .put("dark_green", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_GREEN), 0)) + .put("dark_purple", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_PURPLE), 0)) + .put("dark_red", new Styler((s, o) -> s.applyFormat(ChatFormatting.DARK_RED), 0)) + .put("gold", new Styler((s, o) -> s.applyFormat(ChatFormatting.GOLD), 0)) + .put("gray", new Styler((s, o) -> s.applyFormat(ChatFormatting.GRAY), 0)) + .put("green", new Styler((s, o) -> s.applyFormat(ChatFormatting.GREEN), 0)) + .put("italic", new Styler((s, o) -> s.applyFormat(ChatFormatting.ITALIC), 0)) + .put("light_purple", new Styler((s, o) -> s.applyFormat(ChatFormatting.LIGHT_PURPLE), 0)) + .put("obfuscated", new Styler((s, o) -> s.applyFormat(ChatFormatting.OBFUSCATED), 0)) + .put("red", new Styler((s, o) -> s.applyFormat(ChatFormatting.RED), 0)) + .put("reset", new Styler((s, o) -> s.applyFormat(ChatFormatting.RESET), 0)) + .put("strikethrough", new Styler((s, o) -> s.applyFormat(ChatFormatting.STRIKETHROUGH), 0)) + .put("underline", new Styler((s, o) -> s.applyFormat(ChatFormatting.UNDERLINE), 0)) + .put("white", new Styler((s, o) -> s.applyFormat(ChatFormatting.WHITE), 0)) + .put("yellow", new Styler((s, o) -> s.applyFormat(ChatFormatting.YELLOW), 0)) + + .put("font", new Styler((s, o) -> s.withFont(ResourceLocation.tryParse(o.getFirst())), 1, "alt", "default")) + .put("hex", new Styler((s, o) -> s.withColor(TextColor.fromRgb(Integer.parseInt(o.getFirst(), 16))), 1)) + .put("insert", new Styler((s, o) -> s.withInsertion(o.getFirst()), 1)) + + .put("click", new Styler((s, o) -> s.withClickEvent(parseClickEvent(o.getFirst(), o.get(1))), 2, "change_page", "copy_to_clipboard", "open_file", "open_url", "run_command", "suggest_command")) + .put("hover", new Styler((s, o) -> s.withHoverEvent(parseHoverEvent(o.getFirst(), o.get(1))), 2, "show_entity", "show_item", "show_text")) + + // aliases + .put("strike", new Styler((s, o) -> s.applyFormat(ChatFormatting.STRIKETHROUGH), 0)) + .put("magic", new Styler((s, o) -> s.applyFormat(ChatFormatting.OBFUSCATED), 0)) + .build(); private final StylerFunc styler; private final MutableComponent argument; private final List args; - public FormattedText(StylerFunc styler, MutableComponent argument, List args) { + public StyledComponent(StylerFunc styler, MutableComponent argument, List args) { this.styler = styler; this.argument = argument; this.args = args; diff --git a/src/main/resources/clientcommands.aw b/src/main/resources/clientcommands.aw index 6fe0f89aa..53567ccab 100644 --- a/src/main/resources/clientcommands.aw +++ b/src/main/resources/clientcommands.aw @@ -23,6 +23,7 @@ accessible method net/minecraft/world/entity/projectile/FishingHook canHitEntity # chat accessible method net/minecraft/client/Minecraft openChatScreen (Ljava/lang/String;)V +accessible field net/minecraft/network/chat/TextColor CUSTOM_COLOR_PREFIX Ljava/lang/String; # ckit extendable method net/minecraft/client/gui/screens/inventory/EffectRenderingInventoryScreen renderEffects (Lnet/minecraft/client/gui/GuiGraphics;II)V From 289e67598c9b5ca66413aa4c019dc9b96e7b05a0 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Wed, 25 Sep 2024 15:23:52 +0200 Subject: [PATCH 4/5] FormattedComponentArgumentType -> FormattedComponentArgument --- .../earthcomputer/clientcommands/c2c/C2CPacketHandler.java | 4 ++-- .../clientcommands/command/WhisperEncryptedCommand.java | 2 +- ...entArgumentType.java => FormattedComponentArgument.java} | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/main/java/net/earthcomputer/clientcommands/command/arguments/{FormattedComponentArgumentType.java => FormattedComponentArgument.java} (96%) diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java b/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java index 8413c1eb0..19a7f5c7d 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java @@ -10,7 +10,7 @@ import net.earthcomputer.clientcommands.c2c.packets.PutTicTacToeMarkC2CPacket; import net.earthcomputer.clientcommands.c2c.packets.StartTicTacToeGameC2CPacket; import net.earthcomputer.clientcommands.command.ListenCommand; -import net.earthcomputer.clientcommands.command.arguments.FormattedComponentArgumentType; +import net.earthcomputer.clientcommands.command.arguments.FormattedComponentArgument; import net.earthcomputer.clientcommands.interfaces.IClientPacketListener_C2C; import net.earthcomputer.clientcommands.command.TicTacToeCommand; import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; @@ -192,7 +192,7 @@ public void onMessageC2CPacket(MessageC2CPacket packet) { String message = packet.message(); MutableComponent formattedComponent; try { - formattedComponent = FormattedComponentArgumentType.formattedComponent().parse(new StringReader(message)); + formattedComponent = FormattedComponentArgument.formattedComponent().parse(new StringReader(message)); } catch (CommandSyntaxException e) { formattedComponent = Component.literal(message); } diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java index bbd579c2c..06ce2eecb 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java @@ -14,7 +14,7 @@ import net.minecraft.network.chat.MutableComponent; import static dev.xpple.clientarguments.arguments.CGameProfileArgument.*; -import static net.earthcomputer.clientcommands.command.arguments.FormattedComponentArgumentType.*; +import static net.earthcomputer.clientcommands.command.arguments.FormattedComponentArgument.*; import static net.earthcomputer.clientcommands.command.arguments.WithStringArgument.*; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; diff --git a/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgumentType.java b/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgument.java similarity index 96% rename from src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgumentType.java rename to src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgument.java index 1a0c21c18..3f819ba09 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgumentType.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/arguments/FormattedComponentArgument.java @@ -20,7 +20,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -public class FormattedComponentArgumentType implements ArgumentType { +public class FormattedComponentArgument implements ArgumentType { private static final Collection EXAMPLES = Arrays.asList("Earth", "&lxpple", "&l&o#fb8919nwex"); @@ -29,8 +29,8 @@ public class FormattedComponentArgumentType implements ArgumentType context, String arg) { From 5c161db78ca777e987331491a798919a6e15bff4 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Wed, 25 Sep 2024 15:37:06 +0200 Subject: [PATCH 5/5] Prevent crash exploit --- .../earthcomputer/clientcommands/c2c/C2CPacketHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java b/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java index 19a7f5c7d..92f389ae8 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/C2CPacketHandler.java @@ -190,11 +190,11 @@ public static boolean handleC2CPacket(String content, String sender) { public void onMessageC2CPacket(MessageC2CPacket packet) { String sender = packet.sender(); String message = packet.message(); - MutableComponent formattedComponent; + Component formattedComponent; try { formattedComponent = FormattedComponentArgument.formattedComponent().parse(new StringReader(message)); } catch (CommandSyntaxException e) { - formattedComponent = Component.literal(message); + formattedComponent = Component.nullToEmpty(message); } MutableComponent prefix = Component.empty(); prefix.append(Component.literal("[").withStyle(ChatFormatting.DARK_GRAY));