From 0d98ba755f54da580c2ee8005ce6e857b0d1304b Mon Sep 17 00:00:00 2001 From: Patbox Date: Wed, 10 Apr 2024 13:52:37 +0200 Subject: [PATCH] Simple support for maps in StringArgs, some other fixes/changes --- .../api/arguments/StringArgs.java | 86 +++++++++-- .../api/parsers/tag/SimpleTags.java | 30 ++++ .../api/parsers/tag/TagRegistry.java | 8 +- .../placeholders/api/parsers/tag/TextTag.java | 7 +- .../pb4/placeholders/impl/StringArgOps.java | 138 ++++++++++++++++++ .../impl/textparser/BuiltinTags.java | 53 +++++-- .../textparser/providers/LegacyProvider.java | 1 - .../textparser/tagreg/SimpleTagRegistry.java | 2 + .../java/eu/pb4/placeholderstest/TestMod.java | 13 +- 9 files changed, 300 insertions(+), 38 deletions(-) create mode 100644 src/main/java/eu/pb4/placeholders/api/parsers/tag/SimpleTags.java create mode 100644 src/main/java/eu/pb4/placeholders/impl/StringArgOps.java diff --git a/src/main/java/eu/pb4/placeholders/api/arguments/StringArgs.java b/src/main/java/eu/pb4/placeholders/api/arguments/StringArgs.java index 0e076cd..a2cbe07 100644 --- a/src/main/java/eu/pb4/placeholders/api/arguments/StringArgs.java +++ b/src/main/java/eu/pb4/placeholders/api/arguments/StringArgs.java @@ -1,15 +1,18 @@ package eu.pb4.placeholders.api.arguments; import net.minecraft.util.function.CharPredicate; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.BiConsumer; +import java.util.function.Consumer; public final class StringArgs { private static final StringArgs EMPTY = new StringArgs(""); private final List ordered = new ArrayList<>(); private final Map keyed = new HashMap<>(); + private Map keyedMaps = new HashMap<>(); private final String input; private int currentOrdered = 0; @@ -23,25 +26,25 @@ public static StringArgs ordered(String input, char separator) { } public static StringArgs keyed(String input, char separator, char map) { - return keyed(input, separator, map, SimpleArguments::isWrapCharacter); + return keyed(input, separator, map, true, SimpleArguments::isWrapCharacter); } - public static StringArgs keyed(String input, char separator, char map, CharPredicate wrapCharacters) { + public static StringArgs keyed(String input, char separator, char map, boolean hasMaps, CharPredicate wrapCharacters) { var args = new StringArgs(input); - keyDecomposition(input, separator, map, wrapCharacters, (key, value) -> { + keyDecomposition(input, 0, separator, map, wrapCharacters, hasMaps, (char) 0, (key, value) -> { if (key != null) { args.keyed.put(key, value != null ? SimpleArguments.unwrap(value, wrapCharacters) : ""); } - }); + }, args.keyedMaps::put); return args; } public static StringArgs full(String input, char separator, char map) { - return full(input, separator, map, SimpleArguments::isWrapCharacter); + return full(input, separator, map, true, SimpleArguments::isWrapCharacter); } - public static StringArgs full(String input, char separator, char map, CharPredicate wrapCharacters) { + public static StringArgs full(String input, char separator, char map, boolean hasMaps, CharPredicate wrapCharacters) { var args = new StringArgs(input); - keyDecomposition(input, separator, map, wrapCharacters, (key, value) -> { + keyDecomposition(input, 0, separator, map, wrapCharacters, hasMaps, (char) 0, (key, value) -> { if (key != null) { args.keyed.put(key, value != null ? SimpleArguments.unwrap(value, wrapCharacters) : ""); @@ -49,21 +52,42 @@ public static StringArgs full(String input, char separator, char map, CharPredic args.ordered.add(SimpleArguments.unwrap(key, wrapCharacters)); } } - }); + }, args.keyedMaps::put); return args; } - private static void keyDecomposition(String input, char separator, char map, CharPredicate isWrap, BiConsumer<@Nullable String, @Nullable String> consumer) { + private static int keyDecomposition(String input, int offset, char separator, char map, CharPredicate isWrap, boolean hasMaps, char stopAt, BiConsumer<@Nullable String, @Nullable String> consumer, BiConsumer mapConsumer) { String key = null; String value = null; var b = new StringBuilder(); char wrap = 0; - for (int i = 0; i < input.length(); i++) { + int i = offset; + for (; i < input.length(); i++) { var chr = input.charAt(i); var chrN = i != input.length() - 1 ? input.charAt(i + 1) : 0; - - if (chr == map && key == null) { + if (chr == stopAt && wrap == 0) { + break; + } else if (key != null && b.isEmpty() && hasMaps && (chr == '{' || chr == '[')) { + var arg = new StringArgs(""); + var ti = keyDecomposition(input, i + 1, separator, map, isWrap, true, + chr == '{' ? '}' : ']', (keyx, valuex) -> { + if (keyx != null) { + arg.keyed.put(keyx, valuex != null ? SimpleArguments.unwrap(valuex, isWrap) : ""); + + if (valuex == null) { + arg.ordered.add(SimpleArguments.unwrap(keyx, isWrap)); + } + } + }, arg.keyedMaps::put); + if (ti == input.length()) { + b.append(chr); + } else { + mapConsumer.accept(key, arg); + key = null; + i = ti; + } + } else if (chr == map && key == null) { key = b.toString(); b = new StringBuilder(); } else if ((chr == '\\' && chrN != 0) || (chrN != 0 && chr == chrN && isWrap.test(chr))) { @@ -97,12 +121,18 @@ private static void keyDecomposition(String input, char separator, char map, Cha } else if (!b.isEmpty()) { consumer.accept(b.toString(), null); } + + return i; } public static StringArgs empty() { return EMPTY; } + public static StringArgs emptyNew() { + return new StringArgs(""); + } + public String input() { return input; } @@ -150,6 +180,13 @@ public String getNext(String name, String defaultValue) { return x != null ? x : defaultValue; } + public void ifPresent(String key, Consumer valueConsumer) { + var val = get(key); + if (val != null) { + valueConsumer.accept(val); + } + } + public boolean contains(String key) { return this.keyed.containsKey(key); } @@ -165,4 +202,29 @@ public List ordered() { public int size() { return Math.max(this.keyed.size(), this.ordered.size()); } + + @ApiStatus.Internal + public List unsafeOrdered() { + return this.ordered; + } + + @ApiStatus.Internal + public Map unsafeKeyed() { + return this.keyed; + } + + + @ApiStatus.Internal + public Map unsafeKeyedMap() { + return this.keyedMaps; + } + + @Override + public String toString() { + return "StringArgs{" + + "ordered=" + ordered + + ", keyed=" + keyed + + ", keyedMaps=" + keyedMaps + + '}'; + } } diff --git a/src/main/java/eu/pb4/placeholders/api/parsers/tag/SimpleTags.java b/src/main/java/eu/pb4/placeholders/api/parsers/tag/SimpleTags.java new file mode 100644 index 0000000..39f283b --- /dev/null +++ b/src/main/java/eu/pb4/placeholders/api/parsers/tag/SimpleTags.java @@ -0,0 +1,30 @@ +package eu.pb4.placeholders.api.parsers.tag; + +import eu.pb4.placeholders.api.node.parent.ColorNode; +import eu.pb4.placeholders.api.node.parent.FormattingNode; +import net.minecraft.text.TextColor; +import net.minecraft.util.Formatting; + +import java.util.Collection; + +public final class SimpleTags { + public static TextTag color(String name, Collection aliases, Formatting formatting) { + return TextTag.enclosing( + name, + aliases, + "color", + true, + (nodes, arg, parser) -> new FormattingNode(nodes, formatting) + ); + } + + public static TextTag color(String name, Collection aliases, int rgb) { + return TextTag.enclosing( + name, + aliases, + "color", + true, + (nodes, arg, parser) -> new ColorNode(nodes, TextColor.fromRgb(rgb)) + ); + } +} diff --git a/src/main/java/eu/pb4/placeholders/api/parsers/tag/TagRegistry.java b/src/main/java/eu/pb4/placeholders/api/parsers/tag/TagRegistry.java index 7068022..589877c 100644 --- a/src/main/java/eu/pb4/placeholders/api/parsers/tag/TagRegistry.java +++ b/src/main/java/eu/pb4/placeholders/api/parsers/tag/TagRegistry.java @@ -7,8 +7,8 @@ import java.util.List; public interface TagRegistry { - TagRegistry DEFAULT = new SimpleTagRegistry(true); - TagRegistry SAFE = new SimpleTagRegistry(true); + TagRegistry DEFAULT = SimpleTagRegistry.DEFAULT; + TagRegistry SAFE = SimpleTagRegistry.SAFE; static TagRegistry create() { return new SimpleTagRegistry(false); @@ -51,10 +51,10 @@ static Builder builderWithSafe() { } static void registerDefault(TextTag tag) { - DEFAULT.register(tag); + SimpleTagRegistry.DEFAULT.register(tag); if (tag.userSafe()) { - SAFE.register(tag); + SimpleTagRegistry.SAFE.register(tag); } } diff --git a/src/main/java/eu/pb4/placeholders/api/parsers/tag/TextTag.java b/src/main/java/eu/pb4/placeholders/api/parsers/tag/TextTag.java index 9d1fad9..60a5033 100644 --- a/src/main/java/eu/pb4/placeholders/api/parsers/tag/TextTag.java +++ b/src/main/java/eu/pb4/placeholders/api/parsers/tag/TextTag.java @@ -3,6 +3,7 @@ import eu.pb4.placeholders.api.arguments.StringArgs; import eu.pb4.placeholders.api.node.TextNode; +import java.util.Collection; import java.util.List; import java.util.function.Function; @@ -16,7 +17,7 @@ public static TextTag self(String name, String type, boolean userSafe, Function< return self(name, List.of(), type, userSafe, creator); } - public static TextTag self(String name, List aliases, String type, boolean userSafe, Function creator) { + public static TextTag self(String name, Collection aliases, String type, boolean userSafe, Function creator) { return new TextTag(name, aliases.toArray(new String[0]), type, userSafe, true, NodeCreator.self(creator)); } @@ -28,7 +29,7 @@ public static TextTag self(String name, String type, boolean userSafe, NodeCreat return self(name, List.of(), type, userSafe, creator); } - public static TextTag self(String name, List aliases, String type, boolean userSafe, NodeCreator creator) { + public static TextTag self(String name, Collection aliases, String type, boolean userSafe, NodeCreator creator) { return new TextTag(name, aliases.toArray(new String[0]), type, userSafe, true, creator); } @@ -40,7 +41,7 @@ public static TextTag enclosing(String name, String type, boolean userSafe, Node return enclosing(name, List.of(), type, userSafe, creator); } - public static TextTag enclosing(String name, List aliases, String type, boolean userSafe, NodeCreator creator) { + public static TextTag enclosing(String name, Collection aliases, String type, boolean userSafe, NodeCreator creator) { return new TextTag(name, aliases.toArray(new String[0]), type, userSafe, false, creator); } } diff --git a/src/main/java/eu/pb4/placeholders/impl/StringArgOps.java b/src/main/java/eu/pb4/placeholders/impl/StringArgOps.java new file mode 100644 index 0000000..ed70df1 --- /dev/null +++ b/src/main/java/eu/pb4/placeholders/impl/StringArgOps.java @@ -0,0 +1,138 @@ +package eu.pb4.placeholders.impl; + +import com.google.gson.JsonElement; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.JsonOps; +import eu.pb4.placeholders.api.arguments.StringArgs; + +import java.util.stream.Stream; + +public class StringArgOps implements DynamicOps> { + public static final StringArgOps INSTANCE = new StringArgOps(); + @Override + public Either empty() { + return Either.right(StringArgs.emptyNew()); + } + + @Override + public U convertTo(DynamicOps outOps, Either input) { + return outOps.empty(); + } + + @Override + public DataResult getNumberValue(Either input) { + try { + if (input.left().isPresent()) { + return DataResult.success(Double.valueOf(input.orThrow())); + } + } catch (Throwable ignored) { + return DataResult.success(Boolean.parseBoolean(input.orThrow()) ? 1 : 0); + } + + return DataResult.error(() -> input + " is not a number!"); + } + + @Override + public Either createNumeric(Number i) { + return Either.left(i.toString()); + } + + @Override + public DataResult getStringValue(Either input) { + return input.left().isPresent() ? DataResult.success(input.left().get()) : DataResult.error(() -> input + " is not a number!"); + } + + @Override + public Either createString(String value) { + return Either.left(value); + } + + @Override + public DataResult> mergeToList(Either list, Either value) { + try { + if (value.left().isPresent()) { + list.right().get().unsafeOrdered().add(value.left().orElseThrow()); + } else { + list.right().get().unsafeKeyedMap().put("" + list.right().get().unsafeKeyedMap().size(), value.right().orElseThrow()); + } + + return DataResult.success(list); + } catch (Throwable e) { + return DataResult.error(() -> list + " is not a list!"); + } + } + + @Override + public DataResult> mergeToMap(Either map, Either key, Either value) { + try { + if (value.left().isPresent()) { + map.right().get().unsafeKeyed().put(key.left().orElseThrow(), value.left().orElseThrow()); + } else { + map.right().get().unsafeKeyedMap().put(key.left().orElseThrow(), value.right().orElseThrow()); + } + + return DataResult.success(map); + } catch (Throwable e) { + return DataResult.error(() -> key + " is not a correct key!"); + } + } + + @Override + public DataResult, Either>>> getMapValues(Either input) { + try { + return DataResult.success( + Stream.concat( + input.right().get().unsafeKeyed().entrySet().stream() + .map((e) -> new Pair<>(Either.left(e.getKey()), Either.left(e.getValue()))), + input.right().get().unsafeKeyedMap().entrySet().stream() + .map((e) -> new Pair<>(Either.left(e.getKey()), Either.right(e.getValue()))) + ) + ); + } catch (Throwable e) { + return DataResult.error(() -> input + " is not a map!"); + } + } + + @Override + public Either createMap(Stream, Either>> map) { + var arg = StringArgs.emptyNew(); + map.forEach(x -> x.getSecond() + .ifLeft(y -> arg.unsafeKeyed().put(x.getFirst().left().orElse(""), y)) + .ifRight(y -> arg.unsafeKeyedMap().put(x.getFirst().left().orElse(""), y))); + return Either.right(arg); + } + + @Override + public DataResult>> getStream(Either input) { + return DataResult.success(input.left().isPresent() ? Stream.of(input) : Stream.concat(Stream.concat( + input.right().get().unsafeKeyed().values().stream() + .map(Either::left), + input.right().get().unsafeOrdered().stream() + .map(Either::left)), + input.right().get().unsafeKeyedMap().values().stream() + .map(Either::right) + )); + } + + @Override + public Either createList(Stream> input) { + var arg = StringArgs.emptyNew(); + input.forEach(x -> x.ifLeft(arg.unsafeOrdered()::add) + .ifRight(y -> arg.unsafeKeyedMap().put("" + arg.unsafeKeyedMap().size(), y)) + ); + return Either.right(arg); + } + + @Override + public Either remove(Either input, String key) { + input.ifRight(x -> { + x.unsafeKeyed().remove(key); + x.unsafeKeyedMap().remove(key); + }); + + return input; + } +} diff --git a/src/main/java/eu/pb4/placeholders/impl/textparser/BuiltinTags.java b/src/main/java/eu/pb4/placeholders/impl/textparser/BuiltinTags.java index e3dae1b..8b71920 100644 --- a/src/main/java/eu/pb4/placeholders/impl/textparser/BuiltinTags.java +++ b/src/main/java/eu/pb4/placeholders/impl/textparser/BuiltinTags.java @@ -1,15 +1,16 @@ package eu.pb4.placeholders.impl.textparser; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; +import com.mojang.datafixers.util.Either; import eu.pb4.placeholders.api.arguments.StringArgs; import eu.pb4.placeholders.api.arguments.SimpleArguments; import eu.pb4.placeholders.api.node.*; import eu.pb4.placeholders.api.node.parent.*; +import eu.pb4.placeholders.api.parsers.tag.SimpleTags; import eu.pb4.placeholders.api.parsers.tag.NodeCreator; import eu.pb4.placeholders.api.parsers.tag.TagRegistry; import eu.pb4.placeholders.api.parsers.tag.TextTag; import eu.pb4.placeholders.impl.GeneralUtils; +import eu.pb4.placeholders.impl.StringArgOps; import net.minecraft.entity.EntityType; import net.minecraft.item.ItemStack; import net.minecraft.nbt.StringNbtReader; @@ -25,13 +26,15 @@ @ApiStatus.Internal public final class BuiltinTags { + public static final TextColor DEFAULT_COLOR = TextColor.fromFormatting(Formatting.WHITE); public static void register() { { - Map> aliases = new HashMap<>(); - aliases.put("gold", List.of("orange")); - aliases.put("gray", List.of("grey")); - aliases.put("light_purple", List.of("pink")); - aliases.put("dark_gray", List.of("dark_grey")); + Map> aliases = new HashMap<>(); + aliases.put(Formatting.GOLD, List.of("orange")); + aliases.put(Formatting.GRAY, List.of("grey", "light_gray", "light_grey")); + aliases.put(Formatting.LIGHT_PURPLE, List.of("pink")); + aliases.put(Formatting.DARK_PURPLE, List.of("purple")); + aliases.put(Formatting.DARK_GRAY, List.of("dark_grey")); for (Formatting formatting : Formatting.values()) { if (formatting.isModifier()) { @@ -39,12 +42,10 @@ public static void register() { } TagRegistry.registerDefault( - TextTag.enclosing( + SimpleTags.color( formatting.getName(), - aliases.containsKey(formatting.getName()) ? aliases.get(formatting.getName()) : List.of(), - "color", - true, - (nodes, arg, parser) -> new FormattingNode(nodes, formatting) + aliases.containsKey(formatting) ? aliases.get(formatting) : List.of(), + formatting ) ); } @@ -109,7 +110,9 @@ public static void register() { List.of("colour", "c"), "color", true, - (nodes, data, parser) -> new ColorNode(nodes, TextColor.parse(data.get("value", 0, "white")).get().left().orElse(null)) + (nodes, data, parser) -> { + return new ColorNode(nodes, TextColor.parse(data.get("value", 0, "white")).result().orElse(DEFAULT_COLOR)); + } ) ); } @@ -479,6 +482,24 @@ public static void register() { { TagRegistry.registerDefault( TextTag.enclosing( + "rawstyle", + "special", + false, + (nodes, data, parser) -> { + var x = Style.Codecs.CODEC.decode(StringArgOps.INSTANCE, Either.right(data)); + if (x.error().isPresent()) { + System.out.println(x.error().get().message()); + return TextNode.asSingle(nodes); + } + return new StyledNode(nodes, x.result().get().getFirst(), null, null, null); + } + ) + ); + } + + { + TagRegistry.registerDefault( + TextTag.self( "score", "special", false, (nodes, data, parser) -> { @@ -491,7 +512,7 @@ public static void register() { { TagRegistry.registerDefault( - TextTag.enclosing( + TextTag.self( "selector", "special", false, @@ -507,11 +528,11 @@ public static void register() { { TagRegistry.registerDefault( - TextTag.enclosing( + TextTag.self( "nbt", "special", false, (nodes, data, parser) -> { - var source = data.getNext("source", ""); + String source = data.getNext("source", ""); var cleanLine1 = data.getNext("path", ""); var type = switch (source) { diff --git a/src/main/java/eu/pb4/placeholders/impl/textparser/providers/LegacyProvider.java b/src/main/java/eu/pb4/placeholders/impl/textparser/providers/LegacyProvider.java index d719005..907ff59 100644 --- a/src/main/java/eu/pb4/placeholders/impl/textparser/providers/LegacyProvider.java +++ b/src/main/java/eu/pb4/placeholders/impl/textparser/providers/LegacyProvider.java @@ -41,7 +41,6 @@ public void handleTag(String id, String argument, TagLikeParser.Context context) var tag = registry.getTag(id); assert tag != null; - if (tag.selfContained()) { context.addNode(tag.nodeCreator().createTextNode(TextNode.array(), StringArgs.ordered(argument, ':'), context.parser())); } else { diff --git a/src/main/java/eu/pb4/placeholders/impl/textparser/tagreg/SimpleTagRegistry.java b/src/main/java/eu/pb4/placeholders/impl/textparser/tagreg/SimpleTagRegistry.java index 0d596ad..ef0cc54 100644 --- a/src/main/java/eu/pb4/placeholders/impl/textparser/tagreg/SimpleTagRegistry.java +++ b/src/main/java/eu/pb4/placeholders/impl/textparser/tagreg/SimpleTagRegistry.java @@ -8,6 +8,8 @@ import java.util.*; public final class SimpleTagRegistry implements TagRegistry { + public static final TagRegistry DEFAULT = new SimpleTagRegistry(true); + public static final TagRegistry SAFE = new SimpleTagRegistry(true); static { BuiltinTags.register(); diff --git a/src/testmod/java/eu/pb4/placeholderstest/TestMod.java b/src/testmod/java/eu/pb4/placeholderstest/TestMod.java index 8b79e8f..b678ccc 100644 --- a/src/testmod/java/eu/pb4/placeholderstest/TestMod.java +++ b/src/testmod/java/eu/pb4/placeholderstest/TestMod.java @@ -6,6 +6,7 @@ import eu.pb4.placeholders.api.PlaceholderContext; import eu.pb4.placeholders.api.Placeholders; import eu.pb4.placeholders.api.TextParserUtils; +import eu.pb4.placeholders.api.arguments.StringArgs; import eu.pb4.placeholders.api.node.LiteralNode; import eu.pb4.placeholders.api.node.TextNode; import eu.pb4.placeholders.api.parsers.*; @@ -95,6 +96,12 @@ private static int perf(CommandContext context) { return 0; } + private static int argTest(CommandContext context) { + context.getSource().sendMessage(Text.literal( + StringArgs.full(context.getArgument("arg", String.class), ' ', ':').toString())); + return 0; + } + private static int test(CommandContext context) { try { ServerPlayerEntity player = context.getSource().getPlayer(); @@ -250,7 +257,7 @@ private static int test6x(CommandContext context) { player.sendMessage(Text.literal("STF-V2 | ").append(TagParser.SIMPLIFIED_TEXT_FORMAT.parseText(form, ParserContext.of())), false); player.sendMessage(Text.literal("STF+QT | ").append(TagParser.QUICK_TEXT_WITH_STF.parseText(form, ParserContext.of())), false); player.sendMessage(Text.literal("QT | ").append(TagParser.QUICK_TEXT.parseText(form, ParserContext.of())), false); - } catch (Exception e) { + } catch (Throwable e) { e.printStackTrace(); } return 0; @@ -273,7 +280,9 @@ public void onInitialize() { dispatcher.register( literal("test").then(argument("text", TextArgumentType.text()).executes(TestMod::test)) ); - + dispatcher.register( + literal("argtest").then(argument("arg", StringArgumentType.greedyString()).executes(TestMod::argTest)) + ); dispatcher.register( literal("test2").then(argument("text", StringArgumentType.greedyString()).executes(TestMod::test2)) );