From 42ec58e9544a5dafc3506ca6938a681c942e67fc Mon Sep 17 00:00:00 2001 From: CallMeEchoCodes Date: Fri, 4 Oct 2024 16:18:44 +1000 Subject: [PATCH 1/8] feat: initial textcontent translations --- specter-debug/build.gradle.kts | 10 +- .../impl/debug/SpecterDebugClient.java | 16 +-- .../assets/specter-debug/lang/en_us.json | 104 +++++++++++++++--- .../text/DynamicTextContent.java | 49 +++++++++ .../text/TextContentRegistry.java | 26 +++++ .../SpecterSerializationClient.java | 12 ++ .../StyledTranslatableVisitor.java | 26 +++++ .../TextTranslationSupplier.java | 7 ++ .../serialization/TranslatableVisitor.java | 25 +++++ .../TranslationEntryConsumer.java | 24 ++++ .../serialization/TranslationEntryMap.java | 21 ++++ .../serialization/client/LanguageMixin.java | 44 ++++++++ .../serialization/client/TextCodecsMixin.java | 78 +++++++++++++ .../client/TranslatableTextContentMixin.java | 71 ++++++++++++ .../client/TranslationStorageMixin.java | 69 ++++++++++++ .../specter-serialization.client.mixins.json | 15 +++ .../src/main/resources/fabric.mod.json | 9 +- 17 files changed, 579 insertions(+), 27 deletions(-) create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/StyledTranslatableVisitor.java create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TextTranslationSupplier.java create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslatableVisitor.java create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryConsumer.java create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryMap.java create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java create mode 100644 specter-serialization/src/client/resources/specter-serialization.client.mixins.json diff --git a/specter-debug/build.gradle.kts b/specter-debug/build.gradle.kts index f9648e2..51d64ae 100644 --- a/specter-debug/build.gradle.kts +++ b/specter-debug/build.gradle.kts @@ -1 +1,9 @@ -moduleDependencies(project, "specter-core", "specter-registry", "specter-item", "specter-block") +moduleDependencies( + project, + "specter-core", + "specter-registry", + "specter-item", + "specter-block", + "specter-serialization", + "specter-render" +) diff --git a/specter-debug/src/client/java/dev/spiritstudios/specter/impl/debug/SpecterDebugClient.java b/specter-debug/src/client/java/dev/spiritstudios/specter/impl/debug/SpecterDebugClient.java index b916768..bc9cb4f 100644 --- a/specter-debug/src/client/java/dev/spiritstudios/specter/impl/debug/SpecterDebugClient.java +++ b/specter-debug/src/client/java/dev/spiritstudios/specter/impl/debug/SpecterDebugClient.java @@ -2,27 +2,19 @@ import dev.spiritstudios.specter.api.block.BlockMetatags; import dev.spiritstudios.specter.api.item.ItemMetatags; +import dev.spiritstudios.specter.api.render.RenderMetatags; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; import net.minecraft.block.Block; import net.minecraft.item.tooltip.TooltipType; import net.minecraft.text.Text; -import net.minecraft.util.Formatting; import java.util.List; public class SpecterDebugClient implements ClientModInitializer { - private static Text getMetatagHeader(String key) { - return Text.translatable("tooltip.metatag." + key).formatted(Formatting.DARK_PURPLE); - } - - private static Text getMetatagText(String key, Object... values) { - return Text.translatable("tooltip.metatag." + key + ".text", values).formatted(Formatting.BLUE); - } - private static void addMetatagLine(List lines, String key, Object... values) { - lines.add(getMetatagHeader(key)); - lines.add(getMetatagText(key, values)); + lines.add(Text.translatable("tooltip.metatag." + key)); + lines.add(Text.translatable("tooltip.metatag." + key + ".text", values)); } @Override @@ -40,6 +32,8 @@ public void onInitializeClient() { BlockMetatags.OXIDIZABLE.get(block).ifPresent(entry -> addMetatagLine(lines, "oxidizable", entry.getName())); BlockMetatags.STRIPPABLE.get(block).ifPresent(entry -> addMetatagLine(lines, "strippable", entry.getName())); BlockMetatags.WAXABLE.get(block).ifPresent(entry -> addMetatagLine(lines, "waxable", entry.getName())); + + RenderMetatags.RENDER_LAYER.get(block).ifPresent(entry -> addMetatagLine(lines, "render_layer", entry.asString())); }); } } diff --git a/specter-debug/src/main/resources/assets/specter-debug/lang/en_us.json b/specter-debug/src/main/resources/assets/specter-debug/lang/en_us.json index b42be49..dd285b3 100644 --- a/specter-debug/src/main/resources/assets/specter-debug/lang/en_us.json +++ b/specter-debug/src/main/resources/assets/specter-debug/lang/en_us.json @@ -7,18 +7,94 @@ "commands.heal.not_living": "Cannot heal non-living entity", "commands.heal.success": "Healed %s for %s health", "commands.components.no_components": "No components found", - "tooltip.metatag.flammable": "Flammable:", - "tooltip.metatag.flammable.text": "%s burn chance, %s spread chance", - "tooltip.metatag.flattenable": "Flattens to:", - "tooltip.metatag.flattenable.text": "%s", - "tooltip.metatag.oxidizable": "Oxidizes to:", - "tooltip.metatag.oxidizable.text": "%s", - "tooltip.metatag.strippable": "Strips to:", - "tooltip.metatag.strippable.text": "%s", - "tooltip.metatag.waxable": "Waxes to:", - "tooltip.metatag.waxable.text": "%s", - "tooltip.metatag.fuel": "Fuel burns for:", - "tooltip.metatag.fuel.text": "%s ticks", - "tooltip.metatag.composting_chance": "Composting chance:", - "tooltip.metatag.composting_chance.text": "%s%%" + "tooltip.metatag.flammable": { + "text": "Flammable:", + "color": "dark_purple" + }, + "tooltip.metatag.flammable.text": [ + { + "index": 0, + "color": "blue" + }, + { + "text": " burn chance, ", + "color": "blue" + }, + { + "index": 1, + "color": "blue" + }, + { + "text": " spread chance", + "color": "blue" + } + ], + "tooltip.metatag.flattenable": { + "text": "Flattens to:", + "color": "dark_purple" + }, + "tooltip.metatag.flattenable.text": { + "index": 0, + "color": "blue" + }, + "tooltip.metatag.oxidizable": { + "text": "Oxidizes to:", + "color": "dark_purple" + }, + "tooltip.metatag.oxidizable.text": { + "index": 0, + "color": "blue" + }, + "tooltip.metatag.strippable": { + "text": "Strips to:", + "color": "dark_purple" + }, + "tooltip.metatag.strippable.text": { + "index": 0, + "color": "blue" + }, + "tooltip.metatag.waxable": { + "text": "Waxes to:", + "color": "dark_purple" + }, + "tooltip.metatag.waxable.text": { + "index": 0, + "color": "blue" + }, + "tooltip.metatag.fuel": { + "text": "Fuel burns for:", + "color": "dark_purple" + }, + "tooltip.metatag.fuel.text": [ + { + "index": 0, + "color": "blue" + }, + { + "text": " ticks", + "color": "blue" + } + ], + "tooltip.metatag.composting_chance": { + "text": "Composting chance:", + "color": "dark_purple" + }, + "tooltip.metatag.composting_chance.text": [ + { + "index": 0, + "color": "blue" + }, + { + "text": "%", + "color": "blue" + } + ], + "tooltip.metatag.render_layer": { + "text": "Render Layer:", + "color": "dark_purple" + }, + "tooltip.metatag.render_layer.text": { + "index": 0, + "color": "blue" + } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java new file mode 100644 index 0000000..11927dc --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java @@ -0,0 +1,49 @@ +package dev.spiritstudios.specter.api.serialization.text; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.spiritstudios.specter.impl.serialization.StyledTranslatableVisitor; +import dev.spiritstudios.specter.impl.serialization.TranslatableVisitor; +import net.minecraft.text.StringVisitable; +import net.minecraft.text.Style; +import net.minecraft.text.Text; +import net.minecraft.text.TextContent; + +import java.util.Optional; + +public record DynamicTextContent(int index) implements TextContent { + public static MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group(Codec.INT.fieldOf("index").forGetter(DynamicTextContent::index)).apply(instance, DynamicTextContent::new) + ); + + public static final TextContent.Type TYPE = new Type<>( + CODEC, + "dynamic" + ); + + @Override + public Type getType() { + return TYPE; + } + + @Override + public Optional visit(StringVisitable.Visitor visitor) { + if (!(visitor instanceof TranslatableVisitor translatableVisitor) || translatableVisitor.getArgs().length <= index) + return visitor.accept("{" + index + "}"); + + Object object = translatableVisitor.getArgs()[index]; + if (object instanceof Text text) return text.visit(visitor); + else return visitor.accept(object.toString()); + } + + @Override + public Optional visit(StringVisitable.StyledVisitor visitor, Style style) { + if (!(visitor instanceof StyledTranslatableVisitor translatableVisitor) || translatableVisitor.getArgs().length <= index) + return visitor.accept(style, "{" + index + "}"); + + Object object = translatableVisitor.getArgs()[index]; + if (object instanceof Text text) return text.visit(visitor, style); + else return visitor.accept(style, object.toString()); + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java new file mode 100644 index 0000000..6be114d --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java @@ -0,0 +1,26 @@ +package dev.spiritstudios.specter.api.serialization.text; + +import com.google.common.collect.ImmutableMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.text.TextContent; +import org.jetbrains.annotations.ApiStatus; + +import java.util.Map; + +public final class TextContentRegistry { + private static final Map> types = new Object2ObjectOpenHashMap<>(); + + public static void register(String field, TextContent.Type type) { + types.put(type.id(), new Entry<>(field, type)); + } + + @ApiStatus.Internal + public static Map> getTypes() { + return ImmutableMap.copyOf(types); + } + + @ApiStatus.Internal + public record Entry(String field, TextContent.Type type) { + + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java new file mode 100644 index 0000000..bf05800 --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java @@ -0,0 +1,12 @@ +package dev.spiritstudios.specter.impl.serialization; + +import dev.spiritstudios.specter.api.serialization.text.DynamicTextContent; +import dev.spiritstudios.specter.api.serialization.text.TextContentRegistry; +import net.fabricmc.api.ClientModInitializer; + +public class SpecterSerializationClient implements ClientModInitializer { + @Override + public void onInitializeClient() { + TextContentRegistry.register("index", DynamicTextContent.TYPE); + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/StyledTranslatableVisitor.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/StyledTranslatableVisitor.java new file mode 100644 index 0000000..61bf12b --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/StyledTranslatableVisitor.java @@ -0,0 +1,26 @@ +package dev.spiritstudios.specter.impl.serialization; + +import net.minecraft.text.StringVisitable; +import net.minecraft.text.Style; + +import java.util.Optional; + +public class StyledTranslatableVisitor implements StringVisitable.StyledVisitor { + private final StringVisitable.StyledVisitor visitor; + private final Object[] args; + + public StyledTranslatableVisitor(StringVisitable.StyledVisitor visitor, Object[] args) { + this.visitor = visitor; + this.args = args; + } + + + public Object[] getArgs() { + return args; + } + + @Override + public Optional accept(Style style, String asString) { + return visitor.accept(style, asString); + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TextTranslationSupplier.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TextTranslationSupplier.java new file mode 100644 index 0000000..899bed7 --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TextTranslationSupplier.java @@ -0,0 +1,7 @@ +package dev.spiritstudios.specter.impl.serialization; + +import net.minecraft.text.Text; + +public interface TextTranslationSupplier { + Text specter_serialization$getText(String key); +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslatableVisitor.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslatableVisitor.java new file mode 100644 index 0000000..add5daf --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslatableVisitor.java @@ -0,0 +1,25 @@ +package dev.spiritstudios.specter.impl.serialization; + +import net.minecraft.text.StringVisitable; + +import java.util.Optional; + +public class TranslatableVisitor implements StringVisitable.Visitor { + private final StringVisitable.Visitor visitor; + private final Object[] args; + + public TranslatableVisitor(StringVisitable.Visitor visitor, Object[] args) { + this.visitor = visitor; + this.args = args; + } + + + public Object[] getArgs() { + return args; + } + + @Override + public Optional accept(String asString) { + return visitor.accept(asString); + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryConsumer.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryConsumer.java new file mode 100644 index 0000000..133bf34 --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryConsumer.java @@ -0,0 +1,24 @@ +package dev.spiritstudios.specter.impl.serialization; + +import net.minecraft.text.Text; + +import java.util.function.BiConsumer; + +public class TranslationEntryConsumer implements BiConsumer { + private final BiConsumer stringConsumer; + private final BiConsumer textConsumer; + + public TranslationEntryConsumer(BiConsumer stringConsumer, BiConsumer textConsumer) { + this.stringConsumer = stringConsumer; + this.textConsumer = textConsumer; + } + + @Override + public void accept(String s, String s2) { + stringConsumer.accept(s, s2); + } + + public void accept(String s, Text text) { + textConsumer.accept(s, text); + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryMap.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryMap.java new file mode 100644 index 0000000..a7a45cd --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryMap.java @@ -0,0 +1,21 @@ +package dev.spiritstudios.specter.impl.serialization; + +import com.google.common.collect.ImmutableMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.text.Text; + +import java.util.HashMap; +import java.util.Map; + +// This is so hacky and I love it +public class TranslationEntryMap extends HashMap { + private final Map translations = new Object2ObjectOpenHashMap<>(); + + public void put(String s, Text text) { + translations.put(s, text); + } + + public Map translations() { + return ImmutableMap.copyOf(translations); + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java new file mode 100644 index 0000000..692c1a2 --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java @@ -0,0 +1,44 @@ +package dev.spiritstudios.specter.mixin.serialization.client; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; +import com.mojang.serialization.JsonOps; +import dev.spiritstudios.specter.impl.serialization.TranslationEntryConsumer; +import net.minecraft.text.Text; +import net.minecraft.text.TextCodecs; +import net.minecraft.util.Language; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.function.BiConsumer; + +@Mixin(Language.class) +public class LanguageMixin { + @WrapOperation(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/JsonHelper;asString(Lcom/google/gson/JsonElement;Ljava/lang/String;)Ljava/lang/String;")) + private static String load(JsonElement element, String name, Operation original, @Local(argsOnly = true) BiConsumer entryConsumer, @Share("skip") LocalBooleanRef skip) { + if (element.isJsonPrimitive()) { + skip.set(false); + return original.call(element, name); + } + + if (!(entryConsumer instanceof TranslationEntryConsumer consumer)) + throw new IllegalStateException("Text entry created without a text consumer"); + + Text text = TextCodecs.CODEC.parse(JsonOps.INSTANCE, element).getOrThrow(JsonParseException::new); + consumer.accept(name, text); + + skip.set(true); + return ""; + } + + @WrapOperation(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Ljava/util/function/BiConsumer;accept(Ljava/lang/Object;Ljava/lang/Object;)V")) + private static void skip(BiConsumer instance, T t, U u, Operation original, @Share("skip") LocalBooleanRef skip) { + if (skip.get()) return; + instance.accept(t, u); + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java new file mode 100644 index 0000000..dac9b3c --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java @@ -0,0 +1,78 @@ +package dev.spiritstudios.specter.mixin.serialization.client; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.*; +import dev.spiritstudios.specter.api.serialization.text.TextContentRegistry; +import net.minecraft.text.TextCodecs; +import net.minecraft.text.TextContent; +import net.minecraft.util.StringIdentifiable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +import java.util.function.Supplier; +import java.util.stream.Stream; + +@Mixin(TextCodecs.class) +public class TextCodecsMixin { + @ModifyVariable(method = "dispatchingCodec", at = @At("STORE"), ordinal = 0) + private static MapCodec dispatchingCodec(MapCodec codec, T[] types) { + if (!types.getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return codec; + + return new MapCodec<>() { + @Override + public RecordBuilder encode(E input, DynamicOps ops, RecordBuilder prefix) { + return codec.encode(input, ops, prefix); + } + + @SuppressWarnings("unchecked") + @Override + public DataResult decode(DynamicOps ops, MapLike input) { + DataResult originalResult = codec.decode(ops, input); + if (originalResult.result().isPresent()) return originalResult; + + return TextContentRegistry.getTypes().values().stream() + .filter(entry -> input.get(entry.field()) != null) + .findFirst() + .map(entry -> (DataResult) entry.type().codec().decode(ops, input)) + .orElse(originalResult); + } + + @Override + public Stream keys(DynamicOps ops) { + return Stream.concat( + codec.keys(ops), + TextContentRegistry.getTypes().values().stream().flatMap(entry -> entry.type().codec().keys(ops)) + ); + } + }; + } + + @SuppressWarnings("unchecked") + @WrapOperation(method = "dispatchingCodec", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/StringIdentifiable;createBasicCodec(Ljava/util/function/Supplier;)Lcom/mojang/serialization/Codec;")) + private static Codec dispatchingCodec(Supplier values, Operation> original) { + Codec originalCodec = original.call(values); + if (!values.get().getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return originalCodec; + Codec textContentTypeCodec = Codec.stringResolver(StringIdentifiable::asString, id -> (T) TextContentRegistry.getTypes().get(id).type()); + + return new Codec<>() { + @Override + public DataResult encode(T input, DynamicOps ops, T1 prefix) { + DataResult originalResult = originalCodec.encode(input, ops, prefix); + if (originalResult.result().isPresent()) return originalResult; + + return textContentTypeCodec.encode(input, ops, prefix); + } + + @Override + public DataResult> decode(DynamicOps ops, T1 input) { + DataResult> originalResult = originalCodec.decode(ops, input); + if (originalResult.result().isPresent()) return originalResult; + + return textContentTypeCodec.decode(ops, input); + } + }; + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java new file mode 100644 index 0000000..4a42af5 --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java @@ -0,0 +1,71 @@ +package dev.spiritstudios.specter.mixin.serialization.client; + +import com.google.common.collect.ImmutableList; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import dev.spiritstudios.specter.impl.serialization.StyledTranslatableVisitor; +import dev.spiritstudios.specter.impl.serialization.TextTranslationSupplier; +import dev.spiritstudios.specter.impl.serialization.TranslatableVisitor; +import net.minecraft.text.StringVisitable; +import net.minecraft.text.Style; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableTextContent; +import net.minecraft.util.Language; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; +import java.util.Optional; + +@Mixin(TranslatableTextContent.class) +public abstract class TranslatableTextContentMixin { + @Shadow + @Final + private String key; + + @Shadow + private List translations; + + @Shadow + public abstract Object[] getArgs(); + + @Inject(method = "updateTranslations", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;get(Ljava/lang/String;)Ljava/lang/String;"), cancellable = true) + private void updateTranslations(CallbackInfo ci) { + Language language = Language.getInstance(); + if (!(language instanceof TextTranslationSupplier supplier)) + return; + + Text text = supplier.specter_serialization$getText(key); + if (text == null) return; + + translations = ImmutableList.of(text); + ci.cancel(); + } + + @Inject(method = "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/TranslatableTextContent;updateTranslations()V", shift = At.Shift.AFTER)) + private void visit(StringVisitable.Visitor visitor, CallbackInfoReturnable> cir, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { + translatableVisitorRef.set(new TranslatableVisitor<>(visitor, getArgs())); + } + + @Inject(method = "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/TranslatableTextContent;updateTranslations()V", shift = At.Shift.AFTER)) + private void visit(StringVisitable.StyledVisitor visitor, Style style, CallbackInfoReturnable> cir, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { + translatableVisitorRef.set(new StyledTranslatableVisitor<>(visitor, getArgs())); + } + + @WrapOperation(method = "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/StringVisitable;visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;")) + private Optional visitWrapped(StringVisitable instance, StringVisitable.Visitor tVisitor, Operation> original, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { + return original.call(instance, translatableVisitorRef.get()); + } + + @WrapOperation(method = "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/StringVisitable;visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;")) + private Optional visitWrapped(StringVisitable instance, StringVisitable.StyledVisitor tStyledVisitor, Style style, Operation> original, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { + return original.call(instance, translatableVisitorRef.get(), style); + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java new file mode 100644 index 0000000..e7e3572 --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java @@ -0,0 +1,69 @@ +package dev.spiritstudios.specter.mixin.serialization.client; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import dev.spiritstudios.specter.impl.serialization.TextTranslationSupplier; +import dev.spiritstudios.specter.impl.serialization.TranslationEntryConsumer; +import dev.spiritstudios.specter.impl.serialization.TranslationEntryMap; +import net.minecraft.client.resource.language.TranslationStorage; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +@Mixin(TranslationStorage.class) +public class TranslationStorageMixin implements TextTranslationSupplier { + @Unique + public Map textTranslations; + + @Redirect(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false)) + private static HashMap load() { + return new TranslationEntryMap<>(); + } + + @WrapOperation(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "NEW", target = "(Ljava/util/Map;Z)Lnet/minecraft/client/resource/language/TranslationStorage;")) + private static TranslationStorage skipImmutable(Map translations, boolean rightToLeft, Operation original, @Local Map map) { + if (!(map instanceof TranslationEntryMap translationEntryMap)) + return original.call(translations, rightToLeft); + + TranslationStorage storage = original.call(translationEntryMap, rightToLeft); + ((TranslationStorageMixin) (Object) storage).textTranslations = translationEntryMap.translations(); + return storage; + } + + @ModifyArg(method = "load(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V"), index = 1) + private static BiConsumer load(BiConsumer entryConsumer, @Local(argsOnly = true) Map translations) { + if (!(translations instanceof TranslationEntryMap translationEntryMap)) + return entryConsumer; + + return new TranslationEntryConsumer(entryConsumer, translationEntryMap::put); + } + + @Inject(method = "hasTranslation", at = @At(value = "HEAD"), cancellable = true) + private void hasTranslation(String key, CallbackInfoReturnable cir) { + if (textTranslations == null) return; + if (textTranslations.containsKey(key)) cir.setReturnValue(true); + } + + @Inject(method = "get", at = @At(value = "HEAD"), cancellable = true) + private void get(String key, String fallback, CallbackInfoReturnable cir) { + if (textTranslations == null) return; + Text text = textTranslations.get(key); + if (text != null) cir.setReturnValue(text.getString()); + } + + @Override + public Text specter_serialization$getText(String key) { + if (textTranslations == null) return null; + return textTranslations.get(key); + } +} diff --git a/specter-serialization/src/client/resources/specter-serialization.client.mixins.json b/specter-serialization/src/client/resources/specter-serialization.client.mixins.json new file mode 100644 index 0000000..b0a42d9 --- /dev/null +++ b/specter-serialization/src/client/resources/specter-serialization.client.mixins.json @@ -0,0 +1,15 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.spiritstudios.specter.mixin.serialization.client", + "compatibilityLevel": "JAVA_21", + "client": [ + "LanguageMixin", + "TextCodecsMixin", + "TranslatableTextContentMixin", + "TranslationStorageMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/specter-serialization/src/main/resources/fabric.mod.json b/specter-serialization/src/main/resources/fabric.mod.json index b8d37dc..cfae80c 100644 --- a/specter-serialization/src/main/resources/fabric.mod.json +++ b/specter-serialization/src/main/resources/fabric.mod.json @@ -12,9 +12,16 @@ "license": "MPL-2.0", "environment": "*", "entrypoints": { + "client": [ + "dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient" + ] }, "mixins": [ - "specter-serialization.mixins.json" + "specter-serialization.mixins.json", + { + "config": "specter-serialization.client.mixins.json", + "environment": "client" + } ], "depends": { "fabricloader": ">=${loader_version}", From c8ddc3b7a148b67d57f3e2f9b3b6c3f2f02376ac Mon Sep 17 00:00:00 2001 From: CallMeEchoCodes Date: Fri, 4 Oct 2024 16:35:21 +1000 Subject: [PATCH 2/8] chore: cleanup various things --- .../text/DynamicTextContent.java | 70 ++++++------ .../text/TextContentRegistry.java | 35 +++--- .../{ => text}/StyledTranslatableVisitor.java | 2 +- .../text/TextContentRegistryImpl.java | 22 ++++ .../{ => text}/TextTranslationSupplier.java | 2 +- .../{ => text}/TranslatableVisitor.java | 2 +- .../{ => text}/TranslationEntryConsumer.java | 2 +- .../{ => text}/TranslationEntryMap.java | 2 +- .../serialization/client/LanguageMixin.java | 40 +++---- .../serialization/client/TextCodecsMixin.java | 100 +++++++++--------- .../client/TranslatableTextContentMixin.java | 72 ++++++------- .../client/TranslationStorageMixin.java | 82 +++++++------- 12 files changed, 225 insertions(+), 206 deletions(-) rename specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/{ => text}/StyledTranslatableVisitor.java (90%) create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TextContentRegistryImpl.java rename specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/{ => text}/TextTranslationSupplier.java (68%) rename specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/{ => text}/TranslatableVisitor.java (89%) rename specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/{ => text}/TranslationEntryConsumer.java (91%) rename specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/{ => text}/TranslationEntryMap.java (90%) diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java index 11927dc..6be6937 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java @@ -3,8 +3,8 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.spiritstudios.specter.impl.serialization.StyledTranslatableVisitor; -import dev.spiritstudios.specter.impl.serialization.TranslatableVisitor; +import dev.spiritstudios.specter.impl.serialization.text.StyledTranslatableVisitor; +import dev.spiritstudios.specter.impl.serialization.text.TranslatableVisitor; import net.minecraft.text.StringVisitable; import net.minecraft.text.Style; import net.minecraft.text.Text; @@ -13,37 +13,37 @@ import java.util.Optional; public record DynamicTextContent(int index) implements TextContent { - public static MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group(Codec.INT.fieldOf("index").forGetter(DynamicTextContent::index)).apply(instance, DynamicTextContent::new) - ); - - public static final TextContent.Type TYPE = new Type<>( - CODEC, - "dynamic" - ); - - @Override - public Type getType() { - return TYPE; - } - - @Override - public Optional visit(StringVisitable.Visitor visitor) { - if (!(visitor instanceof TranslatableVisitor translatableVisitor) || translatableVisitor.getArgs().length <= index) - return visitor.accept("{" + index + "}"); - - Object object = translatableVisitor.getArgs()[index]; - if (object instanceof Text text) return text.visit(visitor); - else return visitor.accept(object.toString()); - } - - @Override - public Optional visit(StringVisitable.StyledVisitor visitor, Style style) { - if (!(visitor instanceof StyledTranslatableVisitor translatableVisitor) || translatableVisitor.getArgs().length <= index) - return visitor.accept(style, "{" + index + "}"); - - Object object = translatableVisitor.getArgs()[index]; - if (object instanceof Text text) return text.visit(visitor, style); - else return visitor.accept(style, object.toString()); - } + public static MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group(Codec.INT.fieldOf("index").forGetter(DynamicTextContent::index)).apply(instance, DynamicTextContent::new) + ); + + public static final TextContent.Type TYPE = new Type<>( + CODEC, + "dynamic" + ); + + @Override + public Type getType() { + return TYPE; + } + + @Override + public Optional visit(StringVisitable.Visitor visitor) { + if (!(visitor instanceof TranslatableVisitor translatableVisitor) || translatableVisitor.getArgs().length <= index) + return visitor.accept("{" + index + "}"); + + Object arg = translatableVisitor.getArgs()[index]; + if (arg instanceof Text text) return text.visit(visitor); + return visitor.accept(arg.toString()); + } + + @Override + public Optional visit(StringVisitable.StyledVisitor visitor, Style style) { + if (!(visitor instanceof StyledTranslatableVisitor translatableVisitor) || translatableVisitor.getArgs().length <= index) + return visitor.accept(style, "{" + index + "}"); + + Object arg = translatableVisitor.getArgs()[index]; + if (arg instanceof Text text) return text.visit(visitor, style); + return visitor.accept(style, arg.toString()); + } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java index 6be114d..dda2751 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java @@ -1,26 +1,21 @@ package dev.spiritstudios.specter.api.serialization.text; -import com.google.common.collect.ImmutableMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import dev.spiritstudios.specter.impl.serialization.text.TextContentRegistryImpl; +import net.minecraft.text.TextCodecs; import net.minecraft.text.TextContent; -import org.jetbrains.annotations.ApiStatus; - -import java.util.Map; +/** + * Allows adding your own {@link TextContent.Type} to be encoded and decoded by {@link TextCodecs}. + */ public final class TextContentRegistry { - private static final Map> types = new Object2ObjectOpenHashMap<>(); - - public static void register(String field, TextContent.Type type) { - types.put(type.id(), new Entry<>(field, type)); - } - - @ApiStatus.Internal - public static Map> getTypes() { - return ImmutableMap.copyOf(types); - } - - @ApiStatus.Internal - public record Entry(String field, TextContent.Type type) { - - } + /** + * Registers a new {@link TextContent.Type}. + * This type will be used when either the type field is set to your types id or it contains a field with the name you provided. + * + * @param field The field name + * @param type The type + */ + public static void register(String field, TextContent.Type type) { + TextContentRegistryImpl.register(field, type); + } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/StyledTranslatableVisitor.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/StyledTranslatableVisitor.java similarity index 90% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/StyledTranslatableVisitor.java rename to specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/StyledTranslatableVisitor.java index 61bf12b..077f7ec 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/StyledTranslatableVisitor.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/StyledTranslatableVisitor.java @@ -1,4 +1,4 @@ -package dev.spiritstudios.specter.impl.serialization; +package dev.spiritstudios.specter.impl.serialization.text; import net.minecraft.text.StringVisitable; import net.minecraft.text.Style; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TextContentRegistryImpl.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TextContentRegistryImpl.java new file mode 100644 index 0000000..9a0244e --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TextContentRegistryImpl.java @@ -0,0 +1,22 @@ +package dev.spiritstudios.specter.impl.serialization.text; + +import com.google.common.collect.ImmutableMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.text.TextContent; + +import java.util.Map; + +public final class TextContentRegistryImpl { + private static final Map> types = new Object2ObjectOpenHashMap<>(); + + public static Map> getTypes() { + return ImmutableMap.copyOf(types); + } + + public static void register(String field, TextContent.Type type) { + types.put(type.id(), new Entry<>(field, type)); + } + + public record Entry(String field, TextContent.Type type) { + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TextTranslationSupplier.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TextTranslationSupplier.java similarity index 68% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TextTranslationSupplier.java rename to specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TextTranslationSupplier.java index 899bed7..9cb4b66 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TextTranslationSupplier.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TextTranslationSupplier.java @@ -1,4 +1,4 @@ -package dev.spiritstudios.specter.impl.serialization; +package dev.spiritstudios.specter.impl.serialization.text; import net.minecraft.text.Text; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslatableVisitor.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslatableVisitor.java similarity index 89% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslatableVisitor.java rename to specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslatableVisitor.java index add5daf..4a234a6 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslatableVisitor.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslatableVisitor.java @@ -1,4 +1,4 @@ -package dev.spiritstudios.specter.impl.serialization; +package dev.spiritstudios.specter.impl.serialization.text; import net.minecraft.text.StringVisitable; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryConsumer.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryConsumer.java similarity index 91% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryConsumer.java rename to specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryConsumer.java index 133bf34..1a570fe 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryConsumer.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryConsumer.java @@ -1,4 +1,4 @@ -package dev.spiritstudios.specter.impl.serialization; +package dev.spiritstudios.specter.impl.serialization.text; import net.minecraft.text.Text; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryMap.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryMap.java similarity index 90% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryMap.java rename to specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryMap.java index a7a45cd..2786058 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/TranslationEntryMap.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryMap.java @@ -1,4 +1,4 @@ -package dev.spiritstudios.specter.impl.serialization; +package dev.spiritstudios.specter.impl.serialization.text; import com.google.common.collect.ImmutableMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java index 692c1a2..b8296f1 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java @@ -8,7 +8,7 @@ import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; import com.mojang.serialization.JsonOps; -import dev.spiritstudios.specter.impl.serialization.TranslationEntryConsumer; +import dev.spiritstudios.specter.impl.serialization.text.TranslationEntryConsumer; import net.minecraft.text.Text; import net.minecraft.text.TextCodecs; import net.minecraft.util.Language; @@ -19,26 +19,26 @@ @Mixin(Language.class) public class LanguageMixin { - @WrapOperation(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/JsonHelper;asString(Lcom/google/gson/JsonElement;Ljava/lang/String;)Ljava/lang/String;")) - private static String load(JsonElement element, String name, Operation original, @Local(argsOnly = true) BiConsumer entryConsumer, @Share("skip") LocalBooleanRef skip) { - if (element.isJsonPrimitive()) { - skip.set(false); - return original.call(element, name); - } - - if (!(entryConsumer instanceof TranslationEntryConsumer consumer)) - throw new IllegalStateException("Text entry created without a text consumer"); + @WrapOperation(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/JsonHelper;asString(Lcom/google/gson/JsonElement;Ljava/lang/String;)Ljava/lang/String;")) + private static String load(JsonElement element, String name, Operation original, @Local(argsOnly = true) BiConsumer entryConsumer, @Share("skip") LocalBooleanRef skip) { + if (element.isJsonPrimitive()) { + skip.set(false); + return original.call(element, name); + } - Text text = TextCodecs.CODEC.parse(JsonOps.INSTANCE, element).getOrThrow(JsonParseException::new); - consumer.accept(name, text); + if (!(entryConsumer instanceof TranslationEntryConsumer consumer)) + throw new IllegalStateException("Text entry created without a text consumer"); - skip.set(true); - return ""; - } + Text text = TextCodecs.CODEC.parse(JsonOps.INSTANCE, element).getOrThrow(JsonParseException::new); + consumer.accept(name, text); - @WrapOperation(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Ljava/util/function/BiConsumer;accept(Ljava/lang/Object;Ljava/lang/Object;)V")) - private static void skip(BiConsumer instance, T t, U u, Operation original, @Share("skip") LocalBooleanRef skip) { - if (skip.get()) return; - instance.accept(t, u); - } + skip.set(true); + return ""; + } + + @WrapOperation(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Ljava/util/function/BiConsumer;accept(Ljava/lang/Object;Ljava/lang/Object;)V")) + private static void skip(BiConsumer instance, T t, U u, Operation original, @Share("skip") LocalBooleanRef skip) { + if (skip.get()) return; + instance.accept(t, u); + } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java index dac9b3c..b69024a 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java @@ -4,7 +4,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.*; -import dev.spiritstudios.specter.api.serialization.text.TextContentRegistry; +import dev.spiritstudios.specter.impl.serialization.text.TextContentRegistryImpl; import net.minecraft.text.TextCodecs; import net.minecraft.text.TextContent; import net.minecraft.util.StringIdentifiable; @@ -17,62 +17,62 @@ @Mixin(TextCodecs.class) public class TextCodecsMixin { - @ModifyVariable(method = "dispatchingCodec", at = @At("STORE"), ordinal = 0) - private static MapCodec dispatchingCodec(MapCodec codec, T[] types) { - if (!types.getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return codec; + @ModifyVariable(method = "dispatchingCodec", at = @At("STORE"), ordinal = 0) + private static MapCodec dispatchingCodec(MapCodec codec, T[] types) { + if (!types.getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return codec; - return new MapCodec<>() { - @Override - public RecordBuilder encode(E input, DynamicOps ops, RecordBuilder prefix) { - return codec.encode(input, ops, prefix); - } + return new MapCodec<>() { + @Override + public RecordBuilder encode(E input, DynamicOps ops, RecordBuilder prefix) { + return codec.encode(input, ops, prefix); + } - @SuppressWarnings("unchecked") - @Override - public DataResult decode(DynamicOps ops, MapLike input) { - DataResult originalResult = codec.decode(ops, input); - if (originalResult.result().isPresent()) return originalResult; + @SuppressWarnings("unchecked") + @Override + public DataResult decode(DynamicOps ops, MapLike input) { + DataResult originalResult = codec.decode(ops, input); + if (originalResult.result().isPresent()) return originalResult; - return TextContentRegistry.getTypes().values().stream() - .filter(entry -> input.get(entry.field()) != null) - .findFirst() - .map(entry -> (DataResult) entry.type().codec().decode(ops, input)) - .orElse(originalResult); - } + return TextContentRegistryImpl.getTypes().values().stream() + .filter(entry -> input.get(entry.field()) != null) + .findFirst() + .map(entry -> (DataResult) entry.type().codec().decode(ops, input)) + .orElse(originalResult); + } - @Override - public Stream keys(DynamicOps ops) { - return Stream.concat( - codec.keys(ops), - TextContentRegistry.getTypes().values().stream().flatMap(entry -> entry.type().codec().keys(ops)) - ); - } - }; - } + @Override + public Stream keys(DynamicOps ops) { + return Stream.concat( + codec.keys(ops), + TextContentRegistryImpl.getTypes().values().stream().flatMap(entry -> entry.type().codec().keys(ops)) + ); + } + }; + } - @SuppressWarnings("unchecked") - @WrapOperation(method = "dispatchingCodec", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/StringIdentifiable;createBasicCodec(Ljava/util/function/Supplier;)Lcom/mojang/serialization/Codec;")) - private static Codec dispatchingCodec(Supplier values, Operation> original) { - Codec originalCodec = original.call(values); - if (!values.get().getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return originalCodec; - Codec textContentTypeCodec = Codec.stringResolver(StringIdentifiable::asString, id -> (T) TextContentRegistry.getTypes().get(id).type()); + @SuppressWarnings("unchecked") + @WrapOperation(method = "dispatchingCodec", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/StringIdentifiable;createBasicCodec(Ljava/util/function/Supplier;)Lcom/mojang/serialization/Codec;")) + private static Codec dispatchingCodec(Supplier values, Operation> original) { + Codec originalCodec = original.call(values); + if (!values.get().getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return originalCodec; + Codec textContentTypeCodec = Codec.stringResolver(StringIdentifiable::asString, id -> (T) TextContentRegistryImpl.getTypes().get(id).type()); - return new Codec<>() { - @Override - public DataResult encode(T input, DynamicOps ops, T1 prefix) { - DataResult originalResult = originalCodec.encode(input, ops, prefix); - if (originalResult.result().isPresent()) return originalResult; + return new Codec<>() { + @Override + public DataResult encode(T input, DynamicOps ops, T1 prefix) { + DataResult originalResult = originalCodec.encode(input, ops, prefix); + if (originalResult.result().isPresent()) return originalResult; - return textContentTypeCodec.encode(input, ops, prefix); - } + return textContentTypeCodec.encode(input, ops, prefix); + } - @Override - public DataResult> decode(DynamicOps ops, T1 input) { - DataResult> originalResult = originalCodec.decode(ops, input); - if (originalResult.result().isPresent()) return originalResult; + @Override + public DataResult> decode(DynamicOps ops, T1 input) { + DataResult> originalResult = originalCodec.decode(ops, input); + if (originalResult.result().isPresent()) return originalResult; - return textContentTypeCodec.decode(ops, input); - } - }; - } + return textContentTypeCodec.decode(ops, input); + } + }; + } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java index 4a42af5..28dffdf 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java @@ -5,9 +5,9 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalRef; -import dev.spiritstudios.specter.impl.serialization.StyledTranslatableVisitor; -import dev.spiritstudios.specter.impl.serialization.TextTranslationSupplier; -import dev.spiritstudios.specter.impl.serialization.TranslatableVisitor; +import dev.spiritstudios.specter.impl.serialization.text.StyledTranslatableVisitor; +import dev.spiritstudios.specter.impl.serialization.text.TextTranslationSupplier; +import dev.spiritstudios.specter.impl.serialization.text.TranslatableVisitor; import net.minecraft.text.StringVisitable; import net.minecraft.text.Style; import net.minecraft.text.Text; @@ -26,46 +26,46 @@ @Mixin(TranslatableTextContent.class) public abstract class TranslatableTextContentMixin { - @Shadow - @Final - private String key; + @Shadow + @Final + private String key; - @Shadow - private List translations; + @Shadow + private List translations; - @Shadow - public abstract Object[] getArgs(); + @Shadow + public abstract Object[] getArgs(); - @Inject(method = "updateTranslations", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;get(Ljava/lang/String;)Ljava/lang/String;"), cancellable = true) - private void updateTranslations(CallbackInfo ci) { - Language language = Language.getInstance(); - if (!(language instanceof TextTranslationSupplier supplier)) - return; + @Inject(method = "updateTranslations", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;get(Ljava/lang/String;)Ljava/lang/String;"), cancellable = true) + private void updateTranslations(CallbackInfo ci) { + Language language = Language.getInstance(); + if (!(language instanceof TextTranslationSupplier supplier)) + return; - Text text = supplier.specter_serialization$getText(key); - if (text == null) return; + Text text = supplier.specter_serialization$getText(key); + if (text == null) return; - translations = ImmutableList.of(text); - ci.cancel(); - } + translations = ImmutableList.of(text); + ci.cancel(); + } - @Inject(method = "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/TranslatableTextContent;updateTranslations()V", shift = At.Shift.AFTER)) - private void visit(StringVisitable.Visitor visitor, CallbackInfoReturnable> cir, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { - translatableVisitorRef.set(new TranslatableVisitor<>(visitor, getArgs())); - } + @Inject(method = "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/TranslatableTextContent;updateTranslations()V", shift = At.Shift.AFTER)) + private void visit(StringVisitable.Visitor visitor, CallbackInfoReturnable> cir, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { + translatableVisitorRef.set(new TranslatableVisitor<>(visitor, getArgs())); + } - @Inject(method = "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/TranslatableTextContent;updateTranslations()V", shift = At.Shift.AFTER)) - private void visit(StringVisitable.StyledVisitor visitor, Style style, CallbackInfoReturnable> cir, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { - translatableVisitorRef.set(new StyledTranslatableVisitor<>(visitor, getArgs())); - } + @Inject(method = "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/TranslatableTextContent;updateTranslations()V", shift = At.Shift.AFTER)) + private void visit(StringVisitable.StyledVisitor visitor, Style style, CallbackInfoReturnable> cir, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { + translatableVisitorRef.set(new StyledTranslatableVisitor<>(visitor, getArgs())); + } - @WrapOperation(method = "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/StringVisitable;visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;")) - private Optional visitWrapped(StringVisitable instance, StringVisitable.Visitor tVisitor, Operation> original, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { - return original.call(instance, translatableVisitorRef.get()); - } + @WrapOperation(method = "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/StringVisitable;visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;")) + private Optional visitWrapped(StringVisitable instance, StringVisitable.Visitor tVisitor, Operation> original, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { + return original.call(instance, translatableVisitorRef.get()); + } - @WrapOperation(method = "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/StringVisitable;visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;")) - private Optional visitWrapped(StringVisitable instance, StringVisitable.StyledVisitor tStyledVisitor, Style style, Operation> original, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { - return original.call(instance, translatableVisitorRef.get(), style); - } + @WrapOperation(method = "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/StringVisitable;visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;")) + private Optional visitWrapped(StringVisitable instance, StringVisitable.StyledVisitor tStyledVisitor, Style style, Operation> original, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { + return original.call(instance, translatableVisitorRef.get(), style); + } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java index e7e3572..b0b0839 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java @@ -3,67 +3,69 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; -import dev.spiritstudios.specter.impl.serialization.TextTranslationSupplier; -import dev.spiritstudios.specter.impl.serialization.TranslationEntryConsumer; -import dev.spiritstudios.specter.impl.serialization.TranslationEntryMap; +import dev.spiritstudios.specter.impl.serialization.text.TextTranslationSupplier; +import dev.spiritstudios.specter.impl.serialization.text.TranslationEntryConsumer; +import dev.spiritstudios.specter.impl.serialization.text.TranslationEntryMap; import net.minecraft.client.resource.language.TranslationStorage; import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; @Mixin(TranslationStorage.class) public class TranslationStorageMixin implements TextTranslationSupplier { - @Unique - public Map textTranslations; + @Unique + public Map textTranslations; - @Redirect(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false)) - private static HashMap load() { - return new TranslationEntryMap<>(); - } + @Redirect(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false)) + private static HashMap load() { + return new TranslationEntryMap<>(); + } - @WrapOperation(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "NEW", target = "(Ljava/util/Map;Z)Lnet/minecraft/client/resource/language/TranslationStorage;")) - private static TranslationStorage skipImmutable(Map translations, boolean rightToLeft, Operation original, @Local Map map) { - if (!(map instanceof TranslationEntryMap translationEntryMap)) - return original.call(translations, rightToLeft); + @WrapOperation(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "NEW", target = "(Ljava/util/Map;Z)Lnet/minecraft/client/resource/language/TranslationStorage;")) + private static TranslationStorage skipImmutable(Map translations, boolean rightToLeft, Operation original, @Local Map map) { + if (!(map instanceof TranslationEntryMap translationEntryMap)) + return original.call(translations, rightToLeft); - TranslationStorage storage = original.call(translationEntryMap, rightToLeft); - ((TranslationStorageMixin) (Object) storage).textTranslations = translationEntryMap.translations(); - return storage; - } + TranslationStorage storage = original.call(translationEntryMap, rightToLeft); + ((TranslationStorageMixin) (Object) storage).textTranslations = translationEntryMap.translations(); + return storage; + } - @ModifyArg(method = "load(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V"), index = 1) - private static BiConsumer load(BiConsumer entryConsumer, @Local(argsOnly = true) Map translations) { - if (!(translations instanceof TranslationEntryMap translationEntryMap)) - return entryConsumer; + @WrapOperation(method = "load(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V")) + private static void load(InputStream inputStream, BiConsumer entryConsumer, Operation original, @Local(argsOnly = true) Map translations) { + if (!(translations instanceof TranslationEntryMap translationEntryMap)) { + original.call(inputStream, entryConsumer); + return; + } - return new TranslationEntryConsumer(entryConsumer, translationEntryMap::put); - } + original.call(inputStream, new TranslationEntryConsumer(entryConsumer, translationEntryMap::put)); + } - @Inject(method = "hasTranslation", at = @At(value = "HEAD"), cancellable = true) - private void hasTranslation(String key, CallbackInfoReturnable cir) { - if (textTranslations == null) return; - if (textTranslations.containsKey(key)) cir.setReturnValue(true); - } + @Inject(method = "hasTranslation", at = @At(value = "HEAD"), cancellable = true) + private void hasTranslation(String key, CallbackInfoReturnable cir) { + if (textTranslations == null) return; + if (textTranslations.containsKey(key)) cir.setReturnValue(true); + } - @Inject(method = "get", at = @At(value = "HEAD"), cancellable = true) - private void get(String key, String fallback, CallbackInfoReturnable cir) { - if (textTranslations == null) return; - Text text = textTranslations.get(key); - if (text != null) cir.setReturnValue(text.getString()); - } + @Inject(method = "get", at = @At(value = "HEAD"), cancellable = true) + private void get(String key, String fallback, CallbackInfoReturnable cir) { + if (textTranslations == null) return; + Text text = textTranslations.get(key); + if (text != null) cir.setReturnValue(text.getString()); + } - @Override - public Text specter_serialization$getText(String key) { - if (textTranslations == null) return null; - return textTranslations.get(key); - } + @Override + public Text specter_serialization$getText(String key) { + if (textTranslations == null) return null; + return textTranslations.get(key); + } } From 669db2fd7cc66bc08f43ec1b8457c018dfbd7542 Mon Sep 17 00:00:00 2001 From: CallMeEchoCodes Date: Fri, 4 Oct 2024 16:41:29 +1000 Subject: [PATCH 3/8] chore: javadoc --- .../specter/api/serialization/text/DynamicTextContent.java | 5 +++++ .../specter/api/serialization/text/TextContentRegistry.java | 2 +- .../text/{ => smuggler}/TranslationEntryConsumer.java | 2 +- .../text/{ => smuggler}/TranslationEntryMap.java | 2 +- .../impl/serialization/text/smuggler/package-info.java | 4 ++++ .../specter/mixin/serialization/client/LanguageMixin.java | 2 +- .../mixin/serialization/client/TranslationStorageMixin.java | 4 ++-- 7 files changed, 15 insertions(+), 6 deletions(-) rename specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/{ => smuggler}/TranslationEntryConsumer.java (90%) rename specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/{ => smuggler}/TranslationEntryMap.java (88%) create mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/package-info.java diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java index 6be6937..3309258 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java @@ -12,6 +12,11 @@ import java.util.Optional; +/** + * A text content that is resolved at runtime. + * + * @param index The index of the argument to resolve this content to. + */ public record DynamicTextContent(int index) implements TextContent { public static MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(Codec.INT.fieldOf("index").forGetter(DynamicTextContent::index)).apply(instance, DynamicTextContent::new) diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java index dda2751..1225f6e 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java @@ -10,7 +10,7 @@ public final class TextContentRegistry { /** * Registers a new {@link TextContent.Type}. - * This type will be used when either the type field is set to your types id or it contains a field with the name you provided. + * This type will be used when either the type field is set to the id of your type, or it contains a field with the name you provided. * * @param field The field name * @param type The type diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryConsumer.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryConsumer.java similarity index 90% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryConsumer.java rename to specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryConsumer.java index 1a570fe..a35c4a0 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryConsumer.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryConsumer.java @@ -1,4 +1,4 @@ -package dev.spiritstudios.specter.impl.serialization.text; +package dev.spiritstudios.specter.impl.serialization.text.smuggler; import net.minecraft.text.Text; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryMap.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryMap.java similarity index 88% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryMap.java rename to specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryMap.java index 2786058..714c9ab 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslationEntryMap.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryMap.java @@ -1,4 +1,4 @@ -package dev.spiritstudios.specter.impl.serialization.text; +package dev.spiritstudios.specter.impl.serialization.text.smuggler; import com.google.common.collect.ImmutableMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/package-info.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/package-info.java new file mode 100644 index 0000000..eadf39c --- /dev/null +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/package-info.java @@ -0,0 +1,4 @@ +/** + * This is extremely cursed. Reader beware. + */ +package dev.spiritstudios.specter.impl.serialization.text.smuggler; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java index b8296f1..9ce9b7a 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java @@ -8,7 +8,7 @@ import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; import com.mojang.serialization.JsonOps; -import dev.spiritstudios.specter.impl.serialization.text.TranslationEntryConsumer; +import dev.spiritstudios.specter.impl.serialization.text.smuggler.TranslationEntryConsumer; import net.minecraft.text.Text; import net.minecraft.text.TextCodecs; import net.minecraft.util.Language; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java index b0b0839..850f45b 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java @@ -4,8 +4,8 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import dev.spiritstudios.specter.impl.serialization.text.TextTranslationSupplier; -import dev.spiritstudios.specter.impl.serialization.text.TranslationEntryConsumer; -import dev.spiritstudios.specter.impl.serialization.text.TranslationEntryMap; +import dev.spiritstudios.specter.impl.serialization.text.smuggler.TranslationEntryConsumer; +import dev.spiritstudios.specter.impl.serialization.text.smuggler.TranslationEntryMap; import net.minecraft.client.resource.language.TranslationStorage; import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; From b96822543813f95468ddbd4ce31dffc3bb0b0fa3 Mon Sep 17 00:00:00 2001 From: CallMeEchoCodes Date: Fri, 4 Oct 2024 18:56:40 +1000 Subject: [PATCH 4/8] chore: mixin cleanup --- .../client/TranslationStorageMixin.java | 84 ++++++++++--------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java index 850f45b..4038727 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java @@ -1,5 +1,6 @@ package dev.spiritstudios.specter.mixin.serialization.client; +import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; @@ -11,9 +12,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.io.InputStream; import java.util.HashMap; @@ -22,50 +21,55 @@ @Mixin(TranslationStorage.class) public class TranslationStorageMixin implements TextTranslationSupplier { - @Unique - public Map textTranslations; + @Unique + public Map textTranslations; - @Redirect(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false)) - private static HashMap load() { - return new TranslationEntryMap<>(); - } + /** + * @author CallMeEcho + * @reason No way to do this without overriding the method + */ + @Redirect(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false)) + private static HashMap load() { + return new TranslationEntryMap<>(); + } - @WrapOperation(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "NEW", target = "(Ljava/util/Map;Z)Lnet/minecraft/client/resource/language/TranslationStorage;")) - private static TranslationStorage skipImmutable(Map translations, boolean rightToLeft, Operation original, @Local Map map) { - if (!(map instanceof TranslationEntryMap translationEntryMap)) - return original.call(translations, rightToLeft); + @WrapOperation(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "NEW", target = "(Ljava/util/Map;Z)Lnet/minecraft/client/resource/language/TranslationStorage;")) + private static TranslationStorage skipImmutable(Map translations, boolean rightToLeft, Operation original, @Local Map map) { + if (!(map instanceof TranslationEntryMap translationEntryMap)) + return original.call(translations, rightToLeft); - TranslationStorage storage = original.call(translationEntryMap, rightToLeft); - ((TranslationStorageMixin) (Object) storage).textTranslations = translationEntryMap.translations(); - return storage; - } + TranslationStorage storage = original.call(translationEntryMap, rightToLeft); + ((TranslationStorageMixin) (Object) storage).textTranslations = translationEntryMap.translations(); + return storage; + } - @WrapOperation(method = "load(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V")) - private static void load(InputStream inputStream, BiConsumer entryConsumer, Operation original, @Local(argsOnly = true) Map translations) { - if (!(translations instanceof TranslationEntryMap translationEntryMap)) { - original.call(inputStream, entryConsumer); - return; - } + @WrapOperation(method = "load(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V")) + private static void load(InputStream inputStream, BiConsumer entryConsumer, Operation original, @Local(argsOnly = true) Map translations) { + if (!(translations instanceof TranslationEntryMap translationEntryMap)) { + original.call(inputStream, entryConsumer); + return; + } - original.call(inputStream, new TranslationEntryConsumer(entryConsumer, translationEntryMap::put)); - } + original.call(inputStream, new TranslationEntryConsumer(entryConsumer, translationEntryMap::put)); + } - @Inject(method = "hasTranslation", at = @At(value = "HEAD"), cancellable = true) - private void hasTranslation(String key, CallbackInfoReturnable cir) { - if (textTranslations == null) return; - if (textTranslations.containsKey(key)) cir.setReturnValue(true); - } + @ModifyReturnValue(method = "hasTranslation", at = @At("RETURN")) + private boolean hasTranslation(boolean original, @Local(argsOnly = true) String key) { + if (textTranslations == null) return original; + return original || textTranslations.containsKey(key); + } - @Inject(method = "get", at = @At(value = "HEAD"), cancellable = true) - private void get(String key, String fallback, CallbackInfoReturnable cir) { - if (textTranslations == null) return; - Text text = textTranslations.get(key); - if (text != null) cir.setReturnValue(text.getString()); - } + @ModifyReturnValue(method = "get", at = @At("RETURN")) + private String get(String original) { + if (textTranslations == null) return original; - @Override - public Text specter_serialization$getText(String key) { - if (textTranslations == null) return null; - return textTranslations.get(key); - } + Text text = textTranslations.get(original); + return text != null ? text.getString() : original; + } + + @Override + public Text specter_serialization$getText(String key) { + if (textTranslations == null) return null; + return textTranslations.get(key); + } } From 37149a9121f9213ce9d8553a0876fabee4ded3ba Mon Sep 17 00:00:00 2001 From: CallMeEchoCodes Date: Fri, 4 Oct 2024 19:40:59 +1000 Subject: [PATCH 5/8] feat: refactor to use `ThreadLocal` instead of class smuggling --- .../text/DynamicTextContent.java | 76 +++++++++-------- .../SpecterSerializationClient.java | 10 +++ .../text/StyledTranslatableVisitor.java | 26 ------ .../text/TranslatableVisitor.java | 25 ------ .../smuggler/TranslationEntryConsumer.java | 24 ------ .../text/smuggler/TranslationEntryMap.java | 21 ----- .../text/smuggler/package-info.java | 4 - .../serialization/client/LanguageMixin.java | 37 ++++----- .../client/TranslatableTextContentMixin.java | 81 ++++++++++--------- .../client/TranslationStorageMixin.java | 35 ++------ 10 files changed, 110 insertions(+), 229 deletions(-) delete mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/StyledTranslatableVisitor.java delete mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslatableVisitor.java delete mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryConsumer.java delete mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryMap.java delete mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/package-info.java diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java index 3309258..9428111 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java @@ -3,12 +3,8 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.spiritstudios.specter.impl.serialization.text.StyledTranslatableVisitor; -import dev.spiritstudios.specter.impl.serialization.text.TranslatableVisitor; -import net.minecraft.text.StringVisitable; -import net.minecraft.text.Style; -import net.minecraft.text.Text; -import net.minecraft.text.TextContent; +import dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient; +import net.minecraft.text.*; import java.util.Optional; @@ -18,37 +14,39 @@ * @param index The index of the argument to resolve this content to. */ public record DynamicTextContent(int index) implements TextContent { - public static MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group(Codec.INT.fieldOf("index").forGetter(DynamicTextContent::index)).apply(instance, DynamicTextContent::new) - ); - - public static final TextContent.Type TYPE = new Type<>( - CODEC, - "dynamic" - ); - - @Override - public Type getType() { - return TYPE; - } - - @Override - public Optional visit(StringVisitable.Visitor visitor) { - if (!(visitor instanceof TranslatableVisitor translatableVisitor) || translatableVisitor.getArgs().length <= index) - return visitor.accept("{" + index + "}"); - - Object arg = translatableVisitor.getArgs()[index]; - if (arg instanceof Text text) return text.visit(visitor); - return visitor.accept(arg.toString()); - } - - @Override - public Optional visit(StringVisitable.StyledVisitor visitor, Style style) { - if (!(visitor instanceof StyledTranslatableVisitor translatableVisitor) || translatableVisitor.getArgs().length <= index) - return visitor.accept(style, "{" + index + "}"); - - Object arg = translatableVisitor.getArgs()[index]; - if (arg instanceof Text text) return text.visit(visitor, style); - return visitor.accept(style, arg.toString()); - } + public static MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group(Codec.INT.fieldOf("index").forGetter(DynamicTextContent::index)).apply(instance, DynamicTextContent::new) + ); + + public static final TextContent.Type TYPE = new Type<>( + CODEC, + "dynamic" + ); + + @Override + public Type getType() { + return TYPE; + } + + @Override + public Optional visit(StringVisitable.Visitor visitor) { + TranslatableTextContent parent = SpecterSerializationClient.CURRENT_TRANSLATABLE.get().peek(); + if (parent == null || parent.getArgs().length <= index) + return visitor.accept("{" + index + "}"); + + Object arg = parent.getArgs()[index]; + if (arg instanceof Text text) return text.visit(visitor); + return visitor.accept(arg.toString()); + } + + @Override + public Optional visit(StringVisitable.StyledVisitor visitor, Style style) { + TranslatableTextContent parent = SpecterSerializationClient.CURRENT_TRANSLATABLE.get().peek(); + if (parent == null || parent.getArgs().length <= index) + return visitor.accept(style, "{" + index + "}"); + + Object arg = parent.getArgs()[index]; + if (arg instanceof Text text) return text.visit(visitor, style); + return visitor.accept(style, arg.toString()); + } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java index bf05800..5e0c483 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java @@ -1,10 +1,20 @@ package dev.spiritstudios.specter.impl.serialization; +import com.google.common.collect.ImmutableMap; import dev.spiritstudios.specter.api.serialization.text.DynamicTextContent; import dev.spiritstudios.specter.api.serialization.text.TextContentRegistry; import net.fabricmc.api.ClientModInitializer; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableTextContent; + +import java.util.ArrayDeque; +import java.util.Deque; public class SpecterSerializationClient implements ClientModInitializer { + public static final ThreadLocal> TEXT_TRANSLATIONS_BUILDER = ThreadLocal.withInitial(ImmutableMap.Builder::new); + + public static final ThreadLocal> CURRENT_TRANSLATABLE = ThreadLocal.withInitial(ArrayDeque::new); + @Override public void onInitializeClient() { TextContentRegistry.register("index", DynamicTextContent.TYPE); diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/StyledTranslatableVisitor.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/StyledTranslatableVisitor.java deleted file mode 100644 index 077f7ec..0000000 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/StyledTranslatableVisitor.java +++ /dev/null @@ -1,26 +0,0 @@ -package dev.spiritstudios.specter.impl.serialization.text; - -import net.minecraft.text.StringVisitable; -import net.minecraft.text.Style; - -import java.util.Optional; - -public class StyledTranslatableVisitor implements StringVisitable.StyledVisitor { - private final StringVisitable.StyledVisitor visitor; - private final Object[] args; - - public StyledTranslatableVisitor(StringVisitable.StyledVisitor visitor, Object[] args) { - this.visitor = visitor; - this.args = args; - } - - - public Object[] getArgs() { - return args; - } - - @Override - public Optional accept(Style style, String asString) { - return visitor.accept(style, asString); - } -} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslatableVisitor.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslatableVisitor.java deleted file mode 100644 index 4a234a6..0000000 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TranslatableVisitor.java +++ /dev/null @@ -1,25 +0,0 @@ -package dev.spiritstudios.specter.impl.serialization.text; - -import net.minecraft.text.StringVisitable; - -import java.util.Optional; - -public class TranslatableVisitor implements StringVisitable.Visitor { - private final StringVisitable.Visitor visitor; - private final Object[] args; - - public TranslatableVisitor(StringVisitable.Visitor visitor, Object[] args) { - this.visitor = visitor; - this.args = args; - } - - - public Object[] getArgs() { - return args; - } - - @Override - public Optional accept(String asString) { - return visitor.accept(asString); - } -} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryConsumer.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryConsumer.java deleted file mode 100644 index a35c4a0..0000000 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryConsumer.java +++ /dev/null @@ -1,24 +0,0 @@ -package dev.spiritstudios.specter.impl.serialization.text.smuggler; - -import net.minecraft.text.Text; - -import java.util.function.BiConsumer; - -public class TranslationEntryConsumer implements BiConsumer { - private final BiConsumer stringConsumer; - private final BiConsumer textConsumer; - - public TranslationEntryConsumer(BiConsumer stringConsumer, BiConsumer textConsumer) { - this.stringConsumer = stringConsumer; - this.textConsumer = textConsumer; - } - - @Override - public void accept(String s, String s2) { - stringConsumer.accept(s, s2); - } - - public void accept(String s, Text text) { - textConsumer.accept(s, text); - } -} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryMap.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryMap.java deleted file mode 100644 index 714c9ab..0000000 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/TranslationEntryMap.java +++ /dev/null @@ -1,21 +0,0 @@ -package dev.spiritstudios.specter.impl.serialization.text.smuggler; - -import com.google.common.collect.ImmutableMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.text.Text; - -import java.util.HashMap; -import java.util.Map; - -// This is so hacky and I love it -public class TranslationEntryMap extends HashMap { - private final Map translations = new Object2ObjectOpenHashMap<>(); - - public void put(String s, Text text) { - translations.put(s, text); - } - - public Map translations() { - return ImmutableMap.copyOf(translations); - } -} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/package-info.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/package-info.java deleted file mode 100644 index eadf39c..0000000 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/smuggler/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * This is extremely cursed. Reader beware. - */ -package dev.spiritstudios.specter.impl.serialization.text.smuggler; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java index 9ce9b7a..02db895 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java @@ -8,7 +8,7 @@ import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; import com.mojang.serialization.JsonOps; -import dev.spiritstudios.specter.impl.serialization.text.smuggler.TranslationEntryConsumer; +import dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient; import net.minecraft.text.Text; import net.minecraft.text.TextCodecs; import net.minecraft.util.Language; @@ -19,26 +19,23 @@ @Mixin(Language.class) public class LanguageMixin { - @WrapOperation(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/JsonHelper;asString(Lcom/google/gson/JsonElement;Ljava/lang/String;)Ljava/lang/String;")) - private static String load(JsonElement element, String name, Operation original, @Local(argsOnly = true) BiConsumer entryConsumer, @Share("skip") LocalBooleanRef skip) { - if (element.isJsonPrimitive()) { - skip.set(false); - return original.call(element, name); - } + @WrapOperation(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/JsonHelper;asString(Lcom/google/gson/JsonElement;Ljava/lang/String;)Ljava/lang/String;")) + private static String load(JsonElement element, String name, Operation original, @Local(argsOnly = true) BiConsumer entryConsumer, @Share("skip") LocalBooleanRef skip) { + if (element.isJsonPrimitive()) { + skip.set(false); + return original.call(element, name); + } - if (!(entryConsumer instanceof TranslationEntryConsumer consumer)) - throw new IllegalStateException("Text entry created without a text consumer"); + Text text = TextCodecs.CODEC.parse(JsonOps.INSTANCE, element).getOrThrow(JsonParseException::new); + SpecterSerializationClient.TEXT_TRANSLATIONS_BUILDER.get().put(name, text); - Text text = TextCodecs.CODEC.parse(JsonOps.INSTANCE, element).getOrThrow(JsonParseException::new); - consumer.accept(name, text); + skip.set(true); + return ""; + } - skip.set(true); - return ""; - } - - @WrapOperation(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Ljava/util/function/BiConsumer;accept(Ljava/lang/Object;Ljava/lang/Object;)V")) - private static void skip(BiConsumer instance, T t, U u, Operation original, @Share("skip") LocalBooleanRef skip) { - if (skip.get()) return; - instance.accept(t, u); - } + @WrapOperation(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Ljava/util/function/BiConsumer;accept(Ljava/lang/Object;Ljava/lang/Object;)V")) + private static void skip(BiConsumer instance, T t, U u, Operation original, @Share("skip") LocalBooleanRef skip) { + if (skip.get()) return; + instance.accept(t, u); + } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java index 28dffdf..72ebb44 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java @@ -1,15 +1,9 @@ package dev.spiritstudios.specter.mixin.serialization.client; import com.google.common.collect.ImmutableList; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Share; -import com.llamalad7.mixinextras.sugar.ref.LocalRef; -import dev.spiritstudios.specter.impl.serialization.text.StyledTranslatableVisitor; +import dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient; import dev.spiritstudios.specter.impl.serialization.text.TextTranslationSupplier; -import dev.spiritstudios.specter.impl.serialization.text.TranslatableVisitor; import net.minecraft.text.StringVisitable; -import net.minecraft.text.Style; import net.minecraft.text.Text; import net.minecraft.text.TranslatableTextContent; import net.minecraft.util.Language; @@ -26,46 +20,53 @@ @Mixin(TranslatableTextContent.class) public abstract class TranslatableTextContentMixin { - @Shadow - @Final - private String key; + @Shadow + @Final + private String key; - @Shadow - private List translations; + @Shadow + private List translations; - @Shadow - public abstract Object[] getArgs(); + @Inject( + method = { + "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", + "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;" + }, + at = @At("HEAD") + ) + private void push(CallbackInfoReturnable> cir) { + if (SpecterSerializationClient.CURRENT_TRANSLATABLE.get().contains((TranslatableTextContent) (Object) this)) + throw new IllegalStateException("Detected recursive translation: " + key); - @Inject(method = "updateTranslations", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;get(Ljava/lang/String;)Ljava/lang/String;"), cancellable = true) - private void updateTranslations(CallbackInfo ci) { - Language language = Language.getInstance(); - if (!(language instanceof TextTranslationSupplier supplier)) - return; + SpecterSerializationClient.CURRENT_TRANSLATABLE.get().push((TranslatableTextContent) (Object) this); + } - Text text = supplier.specter_serialization$getText(key); - if (text == null) return; - translations = ImmutableList.of(text); - ci.cancel(); - } + @Inject( + method = { + "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", + "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;" + }, + at = @At("RETURN") + ) + private void pop(CallbackInfoReturnable> cir) { + SpecterSerializationClient.CURRENT_TRANSLATABLE.get().pop(); - @Inject(method = "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/TranslatableTextContent;updateTranslations()V", shift = At.Shift.AFTER)) - private void visit(StringVisitable.Visitor visitor, CallbackInfoReturnable> cir, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { - translatableVisitorRef.set(new TranslatableVisitor<>(visitor, getArgs())); - } + if (SpecterSerializationClient.CURRENT_TRANSLATABLE.get().isEmpty()) + SpecterSerializationClient.CURRENT_TRANSLATABLE.remove(); + } - @Inject(method = "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/TranslatableTextContent;updateTranslations()V", shift = At.Shift.AFTER)) - private void visit(StringVisitable.StyledVisitor visitor, Style style, CallbackInfoReturnable> cir, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { - translatableVisitorRef.set(new StyledTranslatableVisitor<>(visitor, getArgs())); - } - @WrapOperation(method = "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/StringVisitable;visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;")) - private Optional visitWrapped(StringVisitable instance, StringVisitable.Visitor tVisitor, Operation> original, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { - return original.call(instance, translatableVisitorRef.get()); - } + @Inject(method = "updateTranslations", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;get(Ljava/lang/String;)Ljava/lang/String;"), cancellable = true) + private void updateTranslations(CallbackInfo ci) { + Language language = Language.getInstance(); + if (!(language instanceof TextTranslationSupplier supplier)) + return; - @WrapOperation(method = "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/StringVisitable;visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;")) - private Optional visitWrapped(StringVisitable instance, StringVisitable.StyledVisitor tStyledVisitor, Style style, Operation> original, @Share("translatableVisitor") LocalRef> translatableVisitorRef) { - return original.call(instance, translatableVisitorRef.get(), style); - } + Text text = supplier.specter_serialization$getText(key); + if (text == null) return; + + translations = ImmutableList.of(text); + ci.cancel(); + } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java index 4038727..741e9f2 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java @@ -4,55 +4,30 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; +import dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient; import dev.spiritstudios.specter.impl.serialization.text.TextTranslationSupplier; -import dev.spiritstudios.specter.impl.serialization.text.smuggler.TranslationEntryConsumer; -import dev.spiritstudios.specter.impl.serialization.text.smuggler.TranslationEntryMap; import net.minecraft.client.resource.language.TranslationStorage; import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import java.io.InputStream; -import java.util.HashMap; import java.util.Map; -import java.util.function.BiConsumer; @Mixin(TranslationStorage.class) public class TranslationStorageMixin implements TextTranslationSupplier { @Unique - public Map textTranslations; - - /** - * @author CallMeEcho - * @reason No way to do this without overriding the method - */ - @Redirect(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false)) - private static HashMap load() { - return new TranslationEntryMap<>(); - } + private Map textTranslations; @WrapOperation(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "NEW", target = "(Ljava/util/Map;Z)Lnet/minecraft/client/resource/language/TranslationStorage;")) private static TranslationStorage skipImmutable(Map translations, boolean rightToLeft, Operation original, @Local Map map) { - if (!(map instanceof TranslationEntryMap translationEntryMap)) - return original.call(translations, rightToLeft); + TranslationStorage storage = original.call(map, rightToLeft); + ((TranslationStorageMixin) (Object) storage).textTranslations = SpecterSerializationClient.TEXT_TRANSLATIONS_BUILDER.get().build(); - TranslationStorage storage = original.call(translationEntryMap, rightToLeft); - ((TranslationStorageMixin) (Object) storage).textTranslations = translationEntryMap.translations(); + SpecterSerializationClient.TEXT_TRANSLATIONS_BUILDER.remove(); return storage; } - @WrapOperation(method = "load(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V")) - private static void load(InputStream inputStream, BiConsumer entryConsumer, Operation original, @Local(argsOnly = true) Map translations) { - if (!(translations instanceof TranslationEntryMap translationEntryMap)) { - original.call(inputStream, entryConsumer); - return; - } - - original.call(inputStream, new TranslationEntryConsumer(entryConsumer, translationEntryMap::put)); - } - @ModifyReturnValue(method = "hasTranslation", at = @At("RETURN")) private boolean hasTranslation(boolean original, @Local(argsOnly = true) String key) { if (textTranslations == null) return original; From 984fb75c0afdf6a9b2115bb8e468484cb2171a98 Mon Sep 17 00:00:00 2001 From: CallMeEchoCodes Date: Fri, 4 Oct 2024 20:04:57 +1000 Subject: [PATCH 6/8] chore: more mixin cleanup --- .../serialization/client/LanguageMixin.java | 8 +- .../serialization/client/TextCodecsMixin.java | 99 ++++++++++--------- .../client/TranslatableTextContentMixin.java | 13 ++- .../client/TranslationStorageMixin.java | 14 +-- 4 files changed, 68 insertions(+), 66 deletions(-) diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java index 02db895..5497734 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java @@ -2,6 +2,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonParseException; +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; @@ -33,9 +34,8 @@ private static String load(JsonElement element, String name, Operation o return ""; } - @WrapOperation(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Ljava/util/function/BiConsumer;accept(Ljava/lang/Object;Ljava/lang/Object;)V")) - private static void skip(BiConsumer instance, T t, U u, Operation original, @Share("skip") LocalBooleanRef skip) { - if (skip.get()) return; - instance.accept(t, u); + @WrapWithCondition(method = "load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V", at = @At(value = "INVOKE", target = "Ljava/util/function/BiConsumer;accept(Ljava/lang/Object;Ljava/lang/Object;)V")) + private static boolean skip(BiConsumer instance, T t, U u, @Share("skip") LocalBooleanRef skip) { + return !skip.get(); } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java index b69024a..f4ed1f4 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java @@ -17,62 +17,63 @@ @Mixin(TextCodecs.class) public class TextCodecsMixin { - @ModifyVariable(method = "dispatchingCodec", at = @At("STORE"), ordinal = 0) - private static MapCodec dispatchingCodec(MapCodec codec, T[] types) { - if (!types.getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return codec; + @ModifyVariable(method = "dispatchingCodec", at = @At("STORE"), ordinal = 0) + private static MapCodec dispatchingCodec(MapCodec codec, T[] types) { + if (!types.getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return codec; - return new MapCodec<>() { - @Override - public RecordBuilder encode(E input, DynamicOps ops, RecordBuilder prefix) { - return codec.encode(input, ops, prefix); - } + return new MapCodec<>() { + @Override + public RecordBuilder encode(E input, DynamicOps ops, RecordBuilder prefix) { + return codec.encode(input, ops, prefix); + } - @SuppressWarnings("unchecked") - @Override - public DataResult decode(DynamicOps ops, MapLike input) { - DataResult originalResult = codec.decode(ops, input); - if (originalResult.result().isPresent()) return originalResult; + @SuppressWarnings("unchecked") + @Override + public DataResult decode(DynamicOps ops, MapLike input) { + DataResult originalResult = codec.decode(ops, input); + if (originalResult.result().isPresent()) return originalResult; - return TextContentRegistryImpl.getTypes().values().stream() - .filter(entry -> input.get(entry.field()) != null) - .findFirst() - .map(entry -> (DataResult) entry.type().codec().decode(ops, input)) - .orElse(originalResult); - } + return TextContentRegistryImpl.getTypes().values().stream() + .filter(entry -> input.get(entry.field()) != null) + .findFirst() + .map(entry -> (DataResult) entry.type().codec().decode(ops, input)) + .orElse(originalResult); + } - @Override - public Stream keys(DynamicOps ops) { - return Stream.concat( - codec.keys(ops), - TextContentRegistryImpl.getTypes().values().stream().flatMap(entry -> entry.type().codec().keys(ops)) - ); - } - }; - } + @Override + public Stream keys(DynamicOps ops) { + return Stream.concat( + codec.keys(ops), + TextContentRegistryImpl.getTypes().values().stream() + .flatMap(entry -> entry.type().codec().keys(ops)) + ); + } + }; + } - @SuppressWarnings("unchecked") - @WrapOperation(method = "dispatchingCodec", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/StringIdentifiable;createBasicCodec(Ljava/util/function/Supplier;)Lcom/mojang/serialization/Codec;")) - private static Codec dispatchingCodec(Supplier values, Operation> original) { - Codec originalCodec = original.call(values); - if (!values.get().getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return originalCodec; - Codec textContentTypeCodec = Codec.stringResolver(StringIdentifiable::asString, id -> (T) TextContentRegistryImpl.getTypes().get(id).type()); + @SuppressWarnings("unchecked") + @WrapOperation(method = "dispatchingCodec", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/StringIdentifiable;createBasicCodec(Ljava/util/function/Supplier;)Lcom/mojang/serialization/Codec;")) + private static Codec dispatchingCodec(Supplier values, Operation> original) { + Codec originalCodec = original.call(values); + if (!values.get().getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return originalCodec; + Codec textContentTypeCodec = Codec.stringResolver(StringIdentifiable::asString, id -> (T) TextContentRegistryImpl.getTypes().get(id).type()); - return new Codec<>() { - @Override - public DataResult encode(T input, DynamicOps ops, T1 prefix) { - DataResult originalResult = originalCodec.encode(input, ops, prefix); - if (originalResult.result().isPresent()) return originalResult; + return new Codec<>() { + @Override + public DataResult encode(T input, DynamicOps ops, T1 prefix) { + DataResult originalResult = originalCodec.encode(input, ops, prefix); + if (originalResult.result().isPresent()) return originalResult; - return textContentTypeCodec.encode(input, ops, prefix); - } + return textContentTypeCodec.encode(input, ops, prefix); + } - @Override - public DataResult> decode(DynamicOps ops, T1 input) { - DataResult> originalResult = originalCodec.decode(ops, input); - if (originalResult.result().isPresent()) return originalResult; + @Override + public DataResult> decode(DynamicOps ops, T1 input) { + DataResult> originalResult = originalCodec.decode(ops, input); + if (originalResult.result().isPresent()) return originalResult; - return textContentTypeCodec.decode(ops, input); - } - }; - } + return textContentTypeCodec.decode(ops, input); + } + }; + } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java index 72ebb44..d27cf19 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java @@ -1,6 +1,7 @@ package dev.spiritstudios.specter.mixin.serialization.client; import com.google.common.collect.ImmutableList; +import com.llamalad7.mixinextras.sugar.Local; import dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient; import dev.spiritstudios.specter.impl.serialization.text.TextTranslationSupplier; import net.minecraft.text.StringVisitable; @@ -57,9 +58,15 @@ private void pop(CallbackInfoReturnable> cir) { } - @Inject(method = "updateTranslations", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;get(Ljava/lang/String;)Ljava/lang/String;"), cancellable = true) - private void updateTranslations(CallbackInfo ci) { - Language language = Language.getInstance(); + @Inject( + method = "updateTranslations", + at = { + @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;get(Ljava/lang/String;)Ljava/lang/String;"), + @At(value = "INVOKE", target = "Lnet/minecraft/util/Language;get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;") + }, + cancellable = true + ) + private void updateTranslations(CallbackInfo ci, @Local Language language) { if (!(language instanceof TextTranslationSupplier supplier)) return; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java index 741e9f2..89cac7d 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java @@ -1,8 +1,6 @@ package dev.spiritstudios.specter.mixin.serialization.client; import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient; import dev.spiritstudios.specter.impl.serialization.text.TextTranslationSupplier; @@ -19,13 +17,12 @@ public class TranslationStorageMixin implements TextTranslationSupplier { @Unique private Map textTranslations; - @WrapOperation(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "NEW", target = "(Ljava/util/Map;Z)Lnet/minecraft/client/resource/language/TranslationStorage;")) - private static TranslationStorage skipImmutable(Map translations, boolean rightToLeft, Operation original, @Local Map map) { - TranslationStorage storage = original.call(map, rightToLeft); - ((TranslationStorageMixin) (Object) storage).textTranslations = SpecterSerializationClient.TEXT_TRANSLATIONS_BUILDER.get().build(); + @ModifyReturnValue(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At("RETURN")) + private static TranslationStorage load(TranslationStorage original) { + ((TranslationStorageMixin) (Object) original).textTranslations = SpecterSerializationClient.TEXT_TRANSLATIONS_BUILDER.get().build(); SpecterSerializationClient.TEXT_TRANSLATIONS_BUILDER.remove(); - return storage; + return original; } @ModifyReturnValue(method = "hasTranslation", at = @At("RETURN")) @@ -36,15 +33,12 @@ private boolean hasTranslation(boolean original, @Local(argsOnly = true) String @ModifyReturnValue(method = "get", at = @At("RETURN")) private String get(String original) { - if (textTranslations == null) return original; - Text text = textTranslations.get(original); return text != null ? text.getString() : original; } @Override public Text specter_serialization$getText(String key) { - if (textTranslations == null) return null; return textTranslations.get(key); } } From acde4e1522c63805429b007db720bd9022d545f3 Mon Sep 17 00:00:00 2001 From: CallMeEchoCodes Date: Sat, 5 Oct 2024 13:31:14 +1000 Subject: [PATCH 7/8] chore: replace custom codec with `Codec.withAlternative()` --- .../serialization/client/TextCodecsMixin.java | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java index f4ed1f4..2ba1c0a 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java @@ -2,7 +2,6 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.mojang.datafixers.util.Pair; import com.mojang.serialization.*; import dev.spiritstudios.specter.impl.serialization.text.TextContentRegistryImpl; import net.minecraft.text.TextCodecs; @@ -18,22 +17,20 @@ @Mixin(TextCodecs.class) public class TextCodecsMixin { @ModifyVariable(method = "dispatchingCodec", at = @At("STORE"), ordinal = 0) - private static MapCodec dispatchingCodec(MapCodec codec, T[] types) { - if (!types.getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return codec; + private static MapCodec dispatchingCodec(MapCodec original, T[] types) { + if (!types.getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return original; return new MapCodec<>() { @Override public RecordBuilder encode(E input, DynamicOps ops, RecordBuilder prefix) { - return codec.encode(input, ops, prefix); + return original.encode(input, ops, prefix); } @SuppressWarnings("unchecked") @Override public DataResult decode(DynamicOps ops, MapLike input) { - DataResult originalResult = codec.decode(ops, input); - if (originalResult.result().isPresent()) return originalResult; - - return TextContentRegistryImpl.getTypes().values().stream() + DataResult originalResult = original.decode(ops, input); + return originalResult.result().isPresent() ? originalResult : TextContentRegistryImpl.getTypes().values().stream() .filter(entry -> input.get(entry.field()) != null) .findFirst() .map(entry -> (DataResult) entry.type().codec().decode(ops, input)) @@ -43,7 +40,7 @@ public DataResult decode(DynamicOps ops, MapLike input) { @Override public Stream keys(DynamicOps ops) { return Stream.concat( - codec.keys(ops), + original.keys(ops), TextContentRegistryImpl.getTypes().values().stream() .flatMap(entry -> entry.type().codec().keys(ops)) ); @@ -56,24 +53,10 @@ public Stream keys(DynamicOps ops) { private static Codec dispatchingCodec(Supplier values, Operation> original) { Codec originalCodec = original.call(values); if (!values.get().getClass().getComponentType().isAssignableFrom(TextContent.Type.class)) return originalCodec; - Codec textContentTypeCodec = Codec.stringResolver(StringIdentifiable::asString, id -> (T) TextContentRegistryImpl.getTypes().get(id).type()); - - return new Codec<>() { - @Override - public DataResult encode(T input, DynamicOps ops, T1 prefix) { - DataResult originalResult = originalCodec.encode(input, ops, prefix); - if (originalResult.result().isPresent()) return originalResult; - - return textContentTypeCodec.encode(input, ops, prefix); - } - @Override - public DataResult> decode(DynamicOps ops, T1 input) { - DataResult> originalResult = originalCodec.decode(ops, input); - if (originalResult.result().isPresent()) return originalResult; - - return textContentTypeCodec.decode(ops, input); - } - }; + return Codec.withAlternative( + originalCodec, + Codec.stringResolver(StringIdentifiable::asString, id -> (T) TextContentRegistryImpl.getTypes().get(id).type()) + ); } } From e21e5467b448236112e44caabfa1bcd43ee7faf5 Mon Sep 17 00:00:00 2001 From: CallMeEchoCodes Date: Sat, 5 Oct 2024 16:25:21 +1000 Subject: [PATCH 8/8] feat: mixin refactoring and entity gametest --- .../specter/mixin/block/AxeItemMixin.java | 31 ++++++++-------- .../specter/mixin/block/FireBlockMixin.java | 37 +++++++++++++------ .../mixin/block/HoneycombItemMixin.java | 18 ++++++--- .../specter/mixin/block/OxidizableMixin.java | 35 ++++++++++-------- .../specter/mixin/block/ShovelItemMixin.java | 7 ++-- .../testmod/SpecterBlockGameTest.java | 1 - .../testmod/SpecterConfigGameTest.java | 2 + .../core/client/RunArgsNetworkMixin.java | 4 +- .../entity/DefaultAttributeRegistryMixin.java | 29 +++++++-------- .../testmod/SpecterEntityGameTest.java | 18 +++++++++ .../src/testmod/resources/fabric.mod.json | 3 ++ .../client/CreativeInventoryScreenMixin.java | 1 + .../item/AbstractFurnaceBlockEntityMixin.java | 18 +++------ ...omposterBlock$ComposterInventoryMixin.java | 5 +-- .../specter/mixin/item/ItemGroupMixin.java | 17 ++++----- .../specter/mixin/item/ItemGroupsMixin.java | 1 + .../text/TextContentRegistry.java | 21 ----------- .../client/TranslationStorageMixin.java | 6 +-- .../specter-serialization.client.mixins.json | 3 -- .../serialization/format/DynamicFormat.java | 10 ++--- .../text/DynamicTextContent.java | 6 +-- .../text/TextContentRegistry.java | 21 +++++++++++ .../serialization/SpecterSerialization.java} | 6 +-- .../{ => format}/WrappedDynamicFormat.java | 2 +- .../text/TextContentRegistryImpl.java | 0 .../text/TextTranslationSupplier.java | 0 .../mixin/serialization}/LanguageMixin.java | 6 +-- .../mixin/serialization}/TextCodecsMixin.java | 2 +- .../TranslatableTextContentMixin.java | 14 +++---- .../src/main/resources/fabric.mod.json | 4 +- .../specter-serialization.mixins.json | 3 ++ 31 files changed, 184 insertions(+), 147 deletions(-) create mode 100644 specter-entity/src/testmod/java/dev/spiritstudios/testmod/SpecterEntityGameTest.java delete mode 100644 specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java rename specter-serialization/src/{client => main}/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java (88%) create mode 100644 specter-serialization/src/main/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java rename specter-serialization/src/{client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java => main/java/dev/spiritstudios/specter/impl/serialization/SpecterSerialization.java} (82%) rename specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/{ => format}/WrappedDynamicFormat.java (99%) rename specter-serialization/src/{client => main}/java/dev/spiritstudios/specter/impl/serialization/text/TextContentRegistryImpl.java (100%) rename specter-serialization/src/{client => main}/java/dev/spiritstudios/specter/impl/serialization/text/TextTranslationSupplier.java (100%) rename specter-serialization/src/{client/java/dev/spiritstudios/specter/mixin/serialization/client => main/java/dev/spiritstudios/specter/mixin/serialization}/LanguageMixin.java (92%) rename specter-serialization/src/{client/java/dev/spiritstudios/specter/mixin/serialization/client => main/java/dev/spiritstudios/specter/mixin/serialization}/TextCodecsMixin.java (97%) rename specter-serialization/src/{client/java/dev/spiritstudios/specter/mixin/serialization/client => main/java/dev/spiritstudios/specter/mixin/serialization}/TranslatableTextContentMixin.java (82%) diff --git a/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/AxeItemMixin.java b/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/AxeItemMixin.java index 76cc3cb..777ff3b 100644 --- a/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/AxeItemMixin.java +++ b/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/AxeItemMixin.java @@ -2,21 +2,16 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import dev.spiritstudios.specter.api.block.BlockMetatags; import dev.spiritstudios.specter.impl.block.SpecterBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.AxeItem; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Map; import java.util.Optional; @@ -25,19 +20,25 @@ public class AxeItemMixin { @Shadow @Final - public static Map STRIPPED_BLOCKS; + protected static Map STRIPPED_BLOCKS; @WrapOperation(method = "tryStrip", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/AxeItem;getStrippedState(Lnet/minecraft/block/BlockState;)Ljava/util/Optional;")) private Optional getStrippedState(AxeItem instance, BlockState state, Operation> original) { - Optional strippedBlock = BlockMetatags.STRIPPABLE.get(state.getBlock()); - if (strippedBlock.isEmpty()) strippedBlock = Optional.ofNullable(STRIPPED_BLOCKS.get(state.getBlock())); - - return strippedBlock.map(block -> block.getStateWithProperties(state)); + return Optional.ofNullable(STRIPPED_BLOCKS.get(state.getBlock())) + .or(() -> BlockMetatags.STRIPPABLE.get(state.getBlock())) + .map(block -> block.getStateWithProperties(state)) + .or(() -> original.call(instance, state)); } - @Inject(method = "tryStrip", at = @At(value = "INVOKE", target = "Ljava/util/Optional;ofNullable(Ljava/lang/Object;)Ljava/util/Optional;"), cancellable = true) - private void tryStrip(World world, BlockPos pos, @Nullable PlayerEntity player, BlockState state, CallbackInfoReturnable> cir) { - Optional unwaxedBlockState = Optional.ofNullable(SpecterBlock.WAXED_TO_UNWAXED_BLOCKS.get(state.getBlock())).map(unwaxed -> unwaxed.getStateWithProperties(state)); - if (unwaxedBlockState.isPresent()) cir.setReturnValue(unwaxedBlockState); + @WrapOperation(method = "tryStrip", at = @At(value = "INVOKE", target = "Ljava/util/Optional;ofNullable(Ljava/lang/Object;)Ljava/util/Optional;")) + private Optional tryStrip( + Object value, + Operation> original, + @Local(argsOnly = true) BlockState state + ) { + Optional unwaxedBlock = Optional.ofNullable(SpecterBlock.WAXED_TO_UNWAXED_BLOCKS.get(state.getBlock())) + .map(block -> block.getStateWithProperties(state)); + + return unwaxedBlock.or(() -> original.call(value)); } } diff --git a/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/FireBlockMixin.java b/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/FireBlockMixin.java index 2d3fa04..ead0b1b 100644 --- a/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/FireBlockMixin.java +++ b/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/FireBlockMixin.java @@ -1,27 +1,40 @@ package dev.spiritstudios.specter.mixin.block; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import dev.spiritstudios.specter.api.block.BlockMetatags; import dev.spiritstudios.specter.api.block.FlammableBlockData; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.FireBlock; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.Optional; @Mixin(FireBlock.class) public class FireBlockMixin { - @Inject(method = "getBurnChance(Lnet/minecraft/block/BlockState;)I", at = @At("HEAD"), cancellable = true) - private void getBurnChanceFromMetatag(BlockState state, CallbackInfoReturnable cir) { - Optional data = BlockMetatags.FLAMMABLE.get(state.getBlock()); - data.ifPresent(flammableBlockData -> cir.setReturnValue(flammableBlockData.burn())); + @WrapOperation(method = "getBurnChance(Lnet/minecraft/block/BlockState;)I", at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/objects/Object2IntMap;getInt(Ljava/lang/Object;)I", remap = false)) + private int getBurnChanceFromMetatag( + Object2IntMap instance, + Object value, + Operation original, + @Local(argsOnly = true) BlockState state + ) { + return BlockMetatags.FLAMMABLE.get((state).getBlock()) + .map(FlammableBlockData::burn) + .orElse(original.call(instance, value)); } - @Inject(method = "getSpreadChance(Lnet/minecraft/block/BlockState;)I", at = @At("HEAD"), cancellable = true) - private void getSpreadChanceFromMetatag(BlockState state, CallbackInfoReturnable cir) { - Optional data = BlockMetatags.FLAMMABLE.get(state.getBlock()); - data.ifPresent(flammableBlockData -> cir.setReturnValue(flammableBlockData.spread())); + @WrapOperation(method = "getSpreadChance(Lnet/minecraft/block/BlockState;)I", at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/objects/Object2IntMap;getInt(Ljava/lang/Object;)I", remap = false)) + private int getSpreadChanceFromMetatag( + Object2IntMap instance, + Object value, + Operation original, + @Local(argsOnly = true) BlockState state + ) { + return BlockMetatags.FLAMMABLE.get((state).getBlock()) + .map(FlammableBlockData::spread) + .orElse(original.call(instance, value)); } } diff --git a/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/HoneycombItemMixin.java b/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/HoneycombItemMixin.java index f681a12..788cf12 100644 --- a/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/HoneycombItemMixin.java +++ b/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/HoneycombItemMixin.java @@ -1,20 +1,26 @@ package dev.spiritstudios.specter.mixin.block; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import dev.spiritstudios.specter.impl.block.SpecterBlock; import net.minecraft.block.BlockState; import net.minecraft.item.HoneycombItem; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Optional; @Mixin(HoneycombItem.class) public class HoneycombItemMixin { - @Inject(method = "getWaxedState", at = @At("HEAD"), cancellable = true) - private static void getWaxedState(BlockState state, CallbackInfoReturnable> cir) { - Optional waxedBlockState = Optional.ofNullable(SpecterBlock.UNWAXED_TO_WAXED_BLOCKS.get(state.getBlock())).map(block -> block.getStateWithProperties(state)); - if (waxedBlockState.isPresent()) cir.setReturnValue(waxedBlockState); + @WrapOperation(method = "getWaxedState", at = @At(value = "INVOKE", target = "Ljava/util/Optional;ofNullable(Ljava/lang/Object;)Ljava/util/Optional;")) + private static Optional getWaxedState( + Object value, + Operation> original, + @Local(argsOnly = true) BlockState state + ) { + Optional waxedBlockState = Optional.ofNullable(SpecterBlock.UNWAXED_TO_WAXED_BLOCKS.get(state.getBlock())); + + return waxedBlockState.or(() -> original.call(value)); } } diff --git a/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/OxidizableMixin.java b/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/OxidizableMixin.java index 165de11..c652e2d 100644 --- a/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/OxidizableMixin.java +++ b/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/OxidizableMixin.java @@ -3,36 +3,41 @@ import com.google.common.collect.BiMap; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import dev.spiritstudios.specter.impl.block.SpecterBlock; import net.minecraft.block.Block; import net.minecraft.block.Oxidizable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Optional; @Mixin(Oxidizable.class) public interface OxidizableMixin { - @Inject(method = "getIncreasedOxidationBlock", at = @At("HEAD"), cancellable = true) - private static void getIncreasedOxidationBlock(Block block, CallbackInfoReturnable> cir) { - Optional increasedOxidationBlock = Optional.ofNullable(SpecterBlock.OXIDATION_LEVEL_INCREASES.get(block)); - if (increasedOxidationBlock.isPresent()) cir.setReturnValue(increasedOxidationBlock); + @WrapOperation(method = "getIncreasedOxidationBlock", at = @At(value = "INVOKE", target = "Ljava/util/Optional;ofNullable(Ljava/lang/Object;)Ljava/util/Optional;")) + private static Optional getIncreasedOxidationBlock( + Object value, + Operation> original, + @Local(argsOnly = true) Block block + ) { + Optional increasedOxidationBlock = Optional.ofNullable(SpecterBlock.OXIDATION_LEVEL_INCREASES.get(block)); + return increasedOxidationBlock.or(() -> original.call(value)); } - @Inject(method = "getDecreasedOxidationBlock", at = @At("HEAD"), cancellable = true) - private static void getDecreasedOxidationBlock(Block block, CallbackInfoReturnable> cir) { - Optional decreasedOxidationBlock = Optional.ofNullable(SpecterBlock.OXIDATION_LEVEL_DECREASES.get(block)); - if (decreasedOxidationBlock.isPresent()) cir.setReturnValue(decreasedOxidationBlock); + @WrapOperation(method = "getDecreasedOxidationBlock", at = @At(value = "INVOKE", target = "Ljava/util/Optional;ofNullable(Ljava/lang/Object;)Ljava/util/Optional;")) + private static Optional getDecreasedOxidationBlock( + Object value, + Operation> original, + @Local(argsOnly = true) Block block + ) { + Optional decreasedOxidationBlock = Optional.ofNullable(SpecterBlock.OXIDATION_LEVEL_DECREASES.get(block)); + return decreasedOxidationBlock.or(() -> original.call(value)); } - @SuppressWarnings("rawtypes") @WrapOperation(method = "getUnaffectedOxidationBlock", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/BiMap;get(Ljava/lang/Object;)Ljava/lang/Object;", remap = false)) - private static Object getUnaffectedOxidationBlock(BiMap instance, Object o, Operation original) { - Block block = (Block) o; - Optional unaffectedOxidationBlock = Optional.ofNullable(SpecterBlock.OXIDATION_LEVEL_DECREASES.get(block)); - return unaffectedOxidationBlock.orElseGet(() -> (Block) original.call(instance, o)); + private static Object getUnaffectedOxidationBlock(BiMap instance, Object value, Operation original) { + Optional unaffectedOxidationBlock = Optional.ofNullable(SpecterBlock.OXIDATION_LEVEL_DECREASES.get((Block) value)); + return unaffectedOxidationBlock.orElseGet(() -> original.call(instance, value)); } } diff --git a/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/ShovelItemMixin.java b/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/ShovelItemMixin.java index 1c570b2..8b5551c 100644 --- a/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/ShovelItemMixin.java +++ b/specter-block/src/main/java/dev/spiritstudios/specter/mixin/block/ShovelItemMixin.java @@ -4,13 +4,11 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import dev.spiritstudios.specter.api.block.BlockMetatags; import net.minecraft.block.Block; -import net.minecraft.block.BlockState; import net.minecraft.item.ShovelItem; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import java.util.Map; -import java.util.Optional; @Mixin(ShovelItem.class) public class ShovelItemMixin { @@ -19,7 +17,8 @@ public class ShovelItemMixin { private V get(Map instance, Object o, Operation original) { if (!(o instanceof Block block)) return original.call(instance, o); - Optional flattenedBlock = BlockMetatags.FLATTENABLE.get(block); - return flattenedBlock.map(blockState -> (V) blockState).orElseGet(() -> original.call(instance, o)); + return BlockMetatags.FLATTENABLE.get(block) + .map(blockState -> (V) blockState) + .orElseGet(() -> original.call(instance, o)); } } diff --git a/specter-block/src/testmod/java/dev/spiritstudios/testmod/SpecterBlockGameTest.java b/specter-block/src/testmod/java/dev/spiritstudios/testmod/SpecterBlockGameTest.java index 03cec61..8ca1668 100644 --- a/specter-block/src/testmod/java/dev/spiritstudios/testmod/SpecterBlockGameTest.java +++ b/specter-block/src/testmod/java/dev/spiritstudios/testmod/SpecterBlockGameTest.java @@ -74,5 +74,4 @@ public void testOxidizableMetatag(TestContext context) { context.complete(); } - } diff --git a/specter-config/src/testmod/java/dev/spiritstudios/testmod/SpecterConfigGameTest.java b/specter-config/src/testmod/java/dev/spiritstudios/testmod/SpecterConfigGameTest.java index 508232e..2b8b599 100644 --- a/specter-config/src/testmod/java/dev/spiritstudios/testmod/SpecterConfigGameTest.java +++ b/specter-config/src/testmod/java/dev/spiritstudios/testmod/SpecterConfigGameTest.java @@ -34,6 +34,7 @@ public void testTomlConfig(TestContext context) throws IOException { context.assertTrue(TestConfig.TOML_HOLDER.load(), "Config file failed to load"); context.assertTrue(Files.exists(path), "Config file does not exist"); context.assertTrue(TestConfig.TOML_HOLDER.get().testString.get().equals("test2"), "String is not equal to test2, Make sure you haven't modified the config"); + Files.deleteIfExists(path); context.complete(); } @@ -59,6 +60,7 @@ public void testJsonCConfig(TestContext context) throws IOException { context.assertTrue(TestConfig.JSON_HOLDER.load(), "Config file failed to load"); context.assertTrue(Files.exists(path), "Config file does not exist"); context.assertTrue(TestConfig.JSON_HOLDER.get().testString.get().equals("test2"), "String is not equal to test2, Make sure you haven't modified the config"); + Files.deleteIfExists(path); context.complete(); } diff --git a/specter-core/src/client/java/dev/spiritstudios/specter/mixin/core/client/RunArgsNetworkMixin.java b/specter-core/src/client/java/dev/spiritstudios/specter/mixin/core/client/RunArgsNetworkMixin.java index 491aeed..b4678da 100644 --- a/specter-core/src/client/java/dev/spiritstudios/specter/mixin/core/client/RunArgsNetworkMixin.java +++ b/specter-core/src/client/java/dev/spiritstudios/specter/mixin/core/client/RunArgsNetworkMixin.java @@ -23,7 +23,7 @@ public class RunArgsNetworkMixin { @Mutable public Session session; - @Inject(method = "", at = @At("TAIL")) + @Inject(method = "", at = @At("RETURN")) private void init(Session session, PropertyMap userProperties, PropertyMap profileProperties, Proxy proxy, CallbackInfo ci) { if (!SpecterGlobals.DEBUG) return; @@ -35,7 +35,7 @@ private void init(Session session, PropertyMap userProperties, PropertyMap profi String username = System.getProperty("specter.development.username"); UUID uuid = UndashedUuid.fromString(System.getProperty("specter.development.uuid").replace("-", "")); - SpecterGlobals.LOGGER.info(String.format("Using development account %s (%s)", username, uuid)); + SpecterGlobals.LOGGER.info("Using development account {} ({})", username, uuid); this.session = new Session( username, uuid, diff --git a/specter-entity/src/main/java/dev/spiritstudios/specter/mixin/entity/DefaultAttributeRegistryMixin.java b/specter-entity/src/main/java/dev/spiritstudios/specter/mixin/entity/DefaultAttributeRegistryMixin.java index 38f5275..1f060c1 100644 --- a/specter-entity/src/main/java/dev/spiritstudios/specter/mixin/entity/DefaultAttributeRegistryMixin.java +++ b/specter-entity/src/main/java/dev/spiritstudios/specter/mixin/entity/DefaultAttributeRegistryMixin.java @@ -1,10 +1,13 @@ package dev.spiritstudios.specter.mixin.entity; +import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import dev.spiritstudios.specter.api.entity.EntityMetatags; import dev.spiritstudios.specter.impl.entity.DataDefaultAttributeBuilder; import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; import net.minecraft.entity.attribute.DefaultAttributeContainer; import net.minecraft.entity.attribute.DefaultAttributeRegistry; import org.spongepowered.asm.mixin.Mixin; @@ -16,25 +19,21 @@ @Mixin(DefaultAttributeRegistry.class) public class DefaultAttributeRegistryMixin { - @SuppressWarnings("unchecked") - @WrapOperation(method = "get", at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;")) - private static V get(Map instance, Object o, Operation original) { - if (!(o instanceof EntityType entityType)) return original.call(instance, o); - - Optional attributeBuilder = EntityMetatags.DEFAULT_ATTRIBUTES.get(entityType); - if (attributeBuilder.isEmpty()) return original.call(instance, o); - - DefaultAttributeContainer originalAttributes = (DefaultAttributeContainer) original.call(instance, o); - if (originalAttributes == null) return (V) attributeBuilder.get().build(); - - return (V) DataDefaultAttributeBuilder.with(attributeBuilder.get(), originalAttributes).build(); + @ModifyReturnValue(method = "get", at = @At("RETURN")) + private static DefaultAttributeContainer get(DefaultAttributeContainer original, @Local(argsOnly = true) EntityType type) { + Optional originalAttributes = Optional.ofNullable(original); + return EntityMetatags.DEFAULT_ATTRIBUTES.get(type) + .map(builder -> + originalAttributes + .map(attributes -> DataDefaultAttributeBuilder.with(builder, attributes)) + .orElse(builder) + .build() + ).orElse(original); } @WrapOperation(method = "hasDefinitionFor", at = @At(value = "INVOKE", target = "Ljava/util/Map;containsKey(Ljava/lang/Object;)Z")) private static boolean containsKey(Map instance, Object o, Operation original) { if (!(o instanceof EntityType entityType)) return original.call(instance, o); - - boolean hasDefinition = Objects.nonNull(EntityMetatags.DEFAULT_ATTRIBUTES.get(entityType)); - return hasDefinition || original.call(instance, o); + return Objects.nonNull(EntityMetatags.DEFAULT_ATTRIBUTES.get(entityType)) || original.call(instance, o); } } diff --git a/specter-entity/src/testmod/java/dev/spiritstudios/testmod/SpecterEntityGameTest.java b/specter-entity/src/testmod/java/dev/spiritstudios/testmod/SpecterEntityGameTest.java new file mode 100644 index 0000000..796eeb9 --- /dev/null +++ b/specter-entity/src/testmod/java/dev/spiritstudios/testmod/SpecterEntityGameTest.java @@ -0,0 +1,18 @@ +package dev.spiritstudios.testmod; + +import net.fabricmc.fabric.api.gametest.v1.FabricGameTest; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.mob.WardenEntity; +import net.minecraft.test.GameTest; +import net.minecraft.test.TestContext; + +public class SpecterEntityGameTest { + @GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE) + public void testDefaultAttributes(TestContext context) { + WardenEntity warden = context.spawnEntity(EntityType.WARDEN, 0, 0, 0); + warden.damage(context.getWorld().getDamageSources().generic(), 1); + + context.assertTrue(warden.isDead(), "Warden should be dead after being attacked by player"); + context.complete(); + } +} diff --git a/specter-entity/src/testmod/resources/fabric.mod.json b/specter-entity/src/testmod/resources/fabric.mod.json index aefa9e4..35a6d90 100644 --- a/specter-entity/src/testmod/resources/fabric.mod.json +++ b/specter-entity/src/testmod/resources/fabric.mod.json @@ -12,6 +12,9 @@ "license": "MPL-2.0", "environment": "*", "entrypoints": { + "fabric-gametest": [ + "dev.spiritstudios.testmod.SpecterEntityGameTest" + ] }, "depends": { "fabricloader": ">=${loader_version}", diff --git a/specter-item/src/client/java/dev/spiritstudios/specter/mixin/item/client/CreativeInventoryScreenMixin.java b/specter-item/src/client/java/dev/spiritstudios/specter/mixin/item/client/CreativeInventoryScreenMixin.java index fcbab05..6824758 100644 --- a/specter-item/src/client/java/dev/spiritstudios/specter/mixin/item/client/CreativeInventoryScreenMixin.java +++ b/specter-item/src/client/java/dev/spiritstudios/specter/mixin/item/client/CreativeInventoryScreenMixin.java @@ -13,6 +13,7 @@ public class CreativeInventoryScreenMixin { @Inject(method = "init", at = @At("RETURN")) private void init(CallbackInfo ci) { if (!ItemGroupReloader.RELOADED) return; + ((FabricCreativeInventoryScreen) this).switchToPage(0); ItemGroupReloader.RELOADED = false; } diff --git a/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/AbstractFurnaceBlockEntityMixin.java b/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/AbstractFurnaceBlockEntityMixin.java index 1c9d5ca..436fafa 100644 --- a/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/AbstractFurnaceBlockEntityMixin.java +++ b/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/AbstractFurnaceBlockEntityMixin.java @@ -7,21 +7,9 @@ import net.minecraft.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.Optional; @Mixin(AbstractFurnaceBlockEntity.class) public class AbstractFurnaceBlockEntityMixin { - @Inject(method = "getFuelTime", at = @At("HEAD"), cancellable = true) - private void getFuelTime(ItemStack fuel, CallbackInfoReturnable cir) { - if (fuel.isEmpty()) return; - - Optional fuelTime = ItemMetatags.FUEL.get(fuel.getItem()); - fuelTime.ifPresent(cir::setReturnValue); - } - @ModifyReturnValue(method = "canUseAsFuel", at = @At("RETURN")) private static boolean canUseAsFuel(boolean original, @Local(argsOnly = true) ItemStack stack) { if (stack.isEmpty()) return original; @@ -29,4 +17,10 @@ private static boolean canUseAsFuel(boolean original, @Local(argsOnly = true) It boolean hasFuelMetatag = ItemMetatags.FUEL.get(stack.getItem()).isPresent(); return original || hasFuelMetatag; } + + @ModifyReturnValue(method = "getFuelTime", at = @At("RETURN")) + private int getFuelTime(int original, @Local(argsOnly = true) ItemStack fuel) { + if (fuel.isEmpty()) return original; + return ItemMetatags.FUEL.get(fuel.getItem()).orElse(original); + } } diff --git a/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ComposterBlock$ComposterInventoryMixin.java b/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ComposterBlock$ComposterInventoryMixin.java index d4ede3f..861e8bb 100644 --- a/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ComposterBlock$ComposterInventoryMixin.java +++ b/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ComposterBlock$ComposterInventoryMixin.java @@ -12,9 +12,6 @@ public class ComposterBlock$ComposterInventoryMixin { @WrapOperation(method = "canInsert", at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/objects/Object2FloatMap;containsKey(Ljava/lang/Object;)Z", remap = false)) private boolean canInsert(Object2FloatMap instance, Object o, Operation original) { - ItemConvertible itemConvertible = (ItemConvertible) o; - if (SpecterItem.ITEM_TO_LEVEL_INCREASE_CHANCE.containsKey(itemConvertible)) return true; - - return original.call(instance, o); + return SpecterItem.ITEM_TO_LEVEL_INCREASE_CHANCE.containsKey((ItemConvertible) o) || original.call(instance, o); } } diff --git a/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ItemGroupMixin.java b/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ItemGroupMixin.java index 4e688ef..85d6bed 100644 --- a/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ItemGroupMixin.java +++ b/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ItemGroupMixin.java @@ -20,15 +20,14 @@ public abstract class ItemGroupMixin { @ModifyReturnValue(method = {"getDisplayStacks", "getSearchTabStacks"}, at = @At("RETURN")) private Collection getDisplayStacks(Collection original) { - if (this.getType() == ItemGroup.Type.SEARCH) { - List stacks = new ArrayList<>(original); - for (DataItemGroup group : ItemGroupReloader.ITEM_GROUPS) { - if (group.getSearchTabStacks().isEmpty()) continue; - stacks.addAll(group.getSearchTabStacks()); - } - return stacks; - } + if (this.getType() != ItemGroup.Type.SEARCH) return original; - return original; + List stacks = new ArrayList<>(original); + ItemGroupReloader.ITEM_GROUPS.stream() + .map(DataItemGroup::getSearchTabStacks) + .filter(searchTabStacks -> !searchTabStacks.isEmpty()) + .forEach(stacks::addAll); + + return stacks; } } diff --git a/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ItemGroupsMixin.java b/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ItemGroupsMixin.java index 52dc07d..34c1e2a 100644 --- a/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ItemGroupsMixin.java +++ b/specter-item/src/main/java/dev/spiritstudios/specter/mixin/item/ItemGroupsMixin.java @@ -32,6 +32,7 @@ private static List getGroups(List original) { int offset = 0; for (DataItemGroup group : ItemGroupReloader.ITEM_GROUPS) { if (groups.contains(group)) continue; + group.setup(filtered, offset); groups.add(group); offset++; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java deleted file mode 100644 index 1225f6e..0000000 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java +++ /dev/null @@ -1,21 +0,0 @@ -package dev.spiritstudios.specter.api.serialization.text; - -import dev.spiritstudios.specter.impl.serialization.text.TextContentRegistryImpl; -import net.minecraft.text.TextCodecs; -import net.minecraft.text.TextContent; - -/** - * Allows adding your own {@link TextContent.Type} to be encoded and decoded by {@link TextCodecs}. - */ -public final class TextContentRegistry { - /** - * Registers a new {@link TextContent.Type}. - * This type will be used when either the type field is set to the id of your type, or it contains a field with the name you provided. - * - * @param field The field name - * @param type The type - */ - public static void register(String field, TextContent.Type type) { - TextContentRegistryImpl.register(field, type); - } -} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java index 89cac7d..e013e11 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java +++ b/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslationStorageMixin.java @@ -2,7 +2,7 @@ import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.sugar.Local; -import dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient; +import dev.spiritstudios.specter.impl.serialization.SpecterSerialization; import dev.spiritstudios.specter.impl.serialization.text.TextTranslationSupplier; import net.minecraft.client.resource.language.TranslationStorage; import net.minecraft.text.Text; @@ -19,9 +19,9 @@ public class TranslationStorageMixin implements TextTranslationSupplier { @ModifyReturnValue(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At("RETURN")) private static TranslationStorage load(TranslationStorage original) { - ((TranslationStorageMixin) (Object) original).textTranslations = SpecterSerializationClient.TEXT_TRANSLATIONS_BUILDER.get().build(); + ((TranslationStorageMixin) (Object) original).textTranslations = SpecterSerialization.TEXT_TRANSLATIONS_BUILDER.get().build(); - SpecterSerializationClient.TEXT_TRANSLATIONS_BUILDER.remove(); + SpecterSerialization.TEXT_TRANSLATIONS_BUILDER.remove(); return original; } diff --git a/specter-serialization/src/client/resources/specter-serialization.client.mixins.json b/specter-serialization/src/client/resources/specter-serialization.client.mixins.json index b0a42d9..13e6803 100644 --- a/specter-serialization/src/client/resources/specter-serialization.client.mixins.json +++ b/specter-serialization/src/client/resources/specter-serialization.client.mixins.json @@ -4,9 +4,6 @@ "package": "dev.spiritstudios.specter.mixin.serialization.client", "compatibilityLevel": "JAVA_21", "client": [ - "LanguageMixin", - "TextCodecsMixin", - "TranslatableTextContentMixin", "TranslationStorageMixin" ], "injectors": { diff --git a/specter-serialization/src/main/java/dev/spiritstudios/specter/api/serialization/format/DynamicFormat.java b/specter-serialization/src/main/java/dev/spiritstudios/specter/api/serialization/format/DynamicFormat.java index 4e1066c..41f48da 100644 --- a/specter-serialization/src/main/java/dev/spiritstudios/specter/api/serialization/format/DynamicFormat.java +++ b/specter-serialization/src/main/java/dev/spiritstudios/specter/api/serialization/format/DynamicFormat.java @@ -1,7 +1,7 @@ package dev.spiritstudios.specter.api.serialization.format; import com.mojang.serialization.DynamicOps; -import dev.spiritstudios.specter.impl.serialization.WrappedDynamicFormat; +import dev.spiritstudios.specter.impl.serialization.format.WrappedDynamicFormat; import java.io.IOException; import java.io.Reader; @@ -17,6 +17,10 @@ * @param The type this format serializes and deserializes. */ public interface DynamicFormat extends DynamicOps { + static DynamicFormat of(DynamicOps ops, BiConsumer write, Function read, String name) { + return new WrappedDynamicFormat<>(ops, write, read, name); + } + void write(Writer writer, T value) throws IOException; T read(Reader reader) throws IOException; @@ -26,8 +30,4 @@ public interface DynamicFormat extends DynamicOps { default T read(String string) throws IOException { return read(new StringReader(string)); } - - static DynamicFormat of(DynamicOps ops, BiConsumer write, Function read, String name) { - return new WrappedDynamicFormat<>(ops, write, read, name); - } } diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java b/specter-serialization/src/main/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java similarity index 88% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java rename to specter-serialization/src/main/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java index 9428111..6f65140 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java +++ b/specter-serialization/src/main/java/dev/spiritstudios/specter/api/serialization/text/DynamicTextContent.java @@ -3,7 +3,7 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient; +import dev.spiritstudios.specter.impl.serialization.SpecterSerialization; import net.minecraft.text.*; import java.util.Optional; @@ -30,7 +30,7 @@ public Type getType() { @Override public Optional visit(StringVisitable.Visitor visitor) { - TranslatableTextContent parent = SpecterSerializationClient.CURRENT_TRANSLATABLE.get().peek(); + TranslatableTextContent parent = SpecterSerialization.CURRENT_TRANSLATABLE.get().peek(); if (parent == null || parent.getArgs().length <= index) return visitor.accept("{" + index + "}"); @@ -41,7 +41,7 @@ public Optional visit(StringVisitable.Visitor visitor) { @Override public Optional visit(StringVisitable.StyledVisitor visitor, Style style) { - TranslatableTextContent parent = SpecterSerializationClient.CURRENT_TRANSLATABLE.get().peek(); + TranslatableTextContent parent = SpecterSerialization.CURRENT_TRANSLATABLE.get().peek(); if (parent == null || parent.getArgs().length <= index) return visitor.accept(style, "{" + index + "}"); diff --git a/specter-serialization/src/main/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java b/specter-serialization/src/main/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java new file mode 100644 index 0000000..e3baa8e --- /dev/null +++ b/specter-serialization/src/main/java/dev/spiritstudios/specter/api/serialization/text/TextContentRegistry.java @@ -0,0 +1,21 @@ +package dev.spiritstudios.specter.api.serialization.text; + +import dev.spiritstudios.specter.impl.serialization.text.TextContentRegistryImpl; +import net.minecraft.text.TextCodecs; +import net.minecraft.text.TextContent; + +/** + * Allows adding your own {@link TextContent.Type} to be encoded and decoded by {@link TextCodecs}. + */ +public final class TextContentRegistry { + /** + * Registers a new {@link TextContent.Type}. + * This type will be used when either the type field is set to the id of your type, or it contains a field with the name you provided. + * + * @param field The field name + * @param type The type + */ + public static void register(String field, TextContent.Type type) { + TextContentRegistryImpl.register(field, type); + } +} diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java b/specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/SpecterSerialization.java similarity index 82% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java rename to specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/SpecterSerialization.java index 5e0c483..eb7dc0a 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/SpecterSerializationClient.java +++ b/specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/SpecterSerialization.java @@ -3,20 +3,20 @@ import com.google.common.collect.ImmutableMap; import dev.spiritstudios.specter.api.serialization.text.DynamicTextContent; import dev.spiritstudios.specter.api.serialization.text.TextContentRegistry; -import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.api.ModInitializer; import net.minecraft.text.Text; import net.minecraft.text.TranslatableTextContent; import java.util.ArrayDeque; import java.util.Deque; -public class SpecterSerializationClient implements ClientModInitializer { +public class SpecterSerialization implements ModInitializer { public static final ThreadLocal> TEXT_TRANSLATIONS_BUILDER = ThreadLocal.withInitial(ImmutableMap.Builder::new); public static final ThreadLocal> CURRENT_TRANSLATABLE = ThreadLocal.withInitial(ArrayDeque::new); @Override - public void onInitializeClient() { + public void onInitialize() { TextContentRegistry.register("index", DynamicTextContent.TYPE); } } diff --git a/specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/WrappedDynamicFormat.java b/specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/format/WrappedDynamicFormat.java similarity index 99% rename from specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/WrappedDynamicFormat.java rename to specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/format/WrappedDynamicFormat.java index f4d01a1..943a4c8 100644 --- a/specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/WrappedDynamicFormat.java +++ b/specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/format/WrappedDynamicFormat.java @@ -1,4 +1,4 @@ -package dev.spiritstudios.specter.impl.serialization; +package dev.spiritstudios.specter.impl.serialization.format; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.*; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TextContentRegistryImpl.java b/specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/text/TextContentRegistryImpl.java similarity index 100% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TextContentRegistryImpl.java rename to specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/text/TextContentRegistryImpl.java diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TextTranslationSupplier.java b/specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/text/TextTranslationSupplier.java similarity index 100% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/impl/serialization/text/TextTranslationSupplier.java rename to specter-serialization/src/main/java/dev/spiritstudios/specter/impl/serialization/text/TextTranslationSupplier.java diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java b/specter-serialization/src/main/java/dev/spiritstudios/specter/mixin/serialization/LanguageMixin.java similarity index 92% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java rename to specter-serialization/src/main/java/dev/spiritstudios/specter/mixin/serialization/LanguageMixin.java index 5497734..d913c52 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/LanguageMixin.java +++ b/specter-serialization/src/main/java/dev/spiritstudios/specter/mixin/serialization/LanguageMixin.java @@ -1,4 +1,4 @@ -package dev.spiritstudios.specter.mixin.serialization.client; +package dev.spiritstudios.specter.mixin.serialization; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; @@ -9,7 +9,7 @@ import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; import com.mojang.serialization.JsonOps; -import dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient; +import dev.spiritstudios.specter.impl.serialization.SpecterSerialization; import net.minecraft.text.Text; import net.minecraft.text.TextCodecs; import net.minecraft.util.Language; @@ -28,7 +28,7 @@ private static String load(JsonElement element, String name, Operation o } Text text = TextCodecs.CODEC.parse(JsonOps.INSTANCE, element).getOrThrow(JsonParseException::new); - SpecterSerializationClient.TEXT_TRANSLATIONS_BUILDER.get().put(name, text); + SpecterSerialization.TEXT_TRANSLATIONS_BUILDER.get().put(name, text); skip.set(true); return ""; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java b/specter-serialization/src/main/java/dev/spiritstudios/specter/mixin/serialization/TextCodecsMixin.java similarity index 97% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java rename to specter-serialization/src/main/java/dev/spiritstudios/specter/mixin/serialization/TextCodecsMixin.java index 2ba1c0a..ba75ff6 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TextCodecsMixin.java +++ b/specter-serialization/src/main/java/dev/spiritstudios/specter/mixin/serialization/TextCodecsMixin.java @@ -1,4 +1,4 @@ -package dev.spiritstudios.specter.mixin.serialization.client; +package dev.spiritstudios.specter.mixin.serialization; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; diff --git a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java b/specter-serialization/src/main/java/dev/spiritstudios/specter/mixin/serialization/TranslatableTextContentMixin.java similarity index 82% rename from specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java rename to specter-serialization/src/main/java/dev/spiritstudios/specter/mixin/serialization/TranslatableTextContentMixin.java index d27cf19..cf5577e 100644 --- a/specter-serialization/src/client/java/dev/spiritstudios/specter/mixin/serialization/client/TranslatableTextContentMixin.java +++ b/specter-serialization/src/main/java/dev/spiritstudios/specter/mixin/serialization/TranslatableTextContentMixin.java @@ -1,8 +1,8 @@ -package dev.spiritstudios.specter.mixin.serialization.client; +package dev.spiritstudios.specter.mixin.serialization; import com.google.common.collect.ImmutableList; import com.llamalad7.mixinextras.sugar.Local; -import dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient; +import dev.spiritstudios.specter.impl.serialization.SpecterSerialization; import dev.spiritstudios.specter.impl.serialization.text.TextTranslationSupplier; import net.minecraft.text.StringVisitable; import net.minecraft.text.Text; @@ -36,10 +36,10 @@ public abstract class TranslatableTextContentMixin { at = @At("HEAD") ) private void push(CallbackInfoReturnable> cir) { - if (SpecterSerializationClient.CURRENT_TRANSLATABLE.get().contains((TranslatableTextContent) (Object) this)) + if (SpecterSerialization.CURRENT_TRANSLATABLE.get().contains((TranslatableTextContent) (Object) this)) throw new IllegalStateException("Detected recursive translation: " + key); - SpecterSerializationClient.CURRENT_TRANSLATABLE.get().push((TranslatableTextContent) (Object) this); + SpecterSerialization.CURRENT_TRANSLATABLE.get().push((TranslatableTextContent) (Object) this); } @@ -51,10 +51,10 @@ private void push(CallbackInfoReturnable> cir) { at = @At("RETURN") ) private void pop(CallbackInfoReturnable> cir) { - SpecterSerializationClient.CURRENT_TRANSLATABLE.get().pop(); + SpecterSerialization.CURRENT_TRANSLATABLE.get().pop(); - if (SpecterSerializationClient.CURRENT_TRANSLATABLE.get().isEmpty()) - SpecterSerializationClient.CURRENT_TRANSLATABLE.remove(); + if (SpecterSerialization.CURRENT_TRANSLATABLE.get().isEmpty()) + SpecterSerialization.CURRENT_TRANSLATABLE.remove(); } diff --git a/specter-serialization/src/main/resources/fabric.mod.json b/specter-serialization/src/main/resources/fabric.mod.json index cfae80c..92572ba 100644 --- a/specter-serialization/src/main/resources/fabric.mod.json +++ b/specter-serialization/src/main/resources/fabric.mod.json @@ -12,8 +12,8 @@ "license": "MPL-2.0", "environment": "*", "entrypoints": { - "client": [ - "dev.spiritstudios.specter.impl.serialization.SpecterSerializationClient" + "main": [ + "dev.spiritstudios.specter.impl.serialization.SpecterSerialization" ] }, "mixins": [ diff --git a/specter-serialization/src/main/resources/specter-serialization.mixins.json b/specter-serialization/src/main/resources/specter-serialization.mixins.json index e803ce9..dfb5c9b 100644 --- a/specter-serialization/src/main/resources/specter-serialization.mixins.json +++ b/specter-serialization/src/main/resources/specter-serialization.mixins.json @@ -4,6 +4,9 @@ "package": "dev.spiritstudios.specter.mixin.serialization", "compatibilityLevel": "JAVA_21", "mixins": [ + "LanguageMixin", + "TextCodecsMixin", + "TranslatableTextContentMixin" ], "injectors": { "defaultRequire": 1