diff --git a/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_languages/ClientLanguageMixin.java b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_languages/ClientLanguageMixin.java index 01595bdc..0d62a734 100644 --- a/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_languages/ClientLanguageMixin.java +++ b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_languages/ClientLanguageMixin.java @@ -2,7 +2,8 @@ package org.embeddedt.modernfix.common.mixin.perf.dynamic_languages; 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.LocalRef; import net.minecraft.client.resources.language.ClientLanguage; import net.minecraft.server.packs.resources.Resource; import org.embeddedt.modernfix.annotation.ClientOnlyMixin; @@ -11,39 +12,33 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; +import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.function.BiConsumer; +import java.util.Objects; /** * Modifies the language system to load/unload the contents of language entries based on GC pressure. */ -@Mixin(ClientLanguage.class) +@Mixin(value = ClientLanguage.class, priority = 2000) @ClientOnlyMixin public class ClientLanguageMixin { - private static final ThreadLocal MFIX_MODIFY_APPEND_SEMANTICS = ThreadLocal.withInitial(() -> Boolean.FALSE); - /** * @author embeddedt - * @reason modify the semantics of appendFrom so that it's used to do a prepass + * @reason collect the list of all known language resources */ - @ModifyArg(method = "appendFrom", at = @At(value = "INVOKE", target = "Lnet/minecraft/locale/Language;loadFromJson(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V"), index = 1) - private static BiConsumer changeSemanticsOfConsumer(BiConsumer consumer, @Local(ordinal = 0, argsOnly = true) Map destinationMap, @Local(ordinal = 0) Resource resource) { - return MFIX_MODIFY_APPEND_SEMANTICS.get() ? ((k, v) -> destinationMap.put(k, resource)) : consumer; - } - - /** - * @author embeddedt - * @reason collect resources that own keys with a prepass - */ - @WrapOperation(method = "loadFrom", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/language/ClientLanguage;appendFrom(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V")) - private static void trackEntrySource(String languageName, List resources, Map destinationMap, Operation original) { - MFIX_MODIFY_APPEND_SEMANTICS.set(true); - try { - original.call(languageName, resources, destinationMap); - } finally { - MFIX_MODIFY_APPEND_SEMANTICS.remove(); + @WrapOperation(method = "loadFrom", at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/resources/language/ClientLanguage;appendFrom(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V")) + private static void collectResources(String languageName, List resources, + Map destinationMap, Operation original, + @Share("usedResources") LocalRef> usedResources) { + List collected = usedResources.get(); + if (collected == null) { + collected = new ArrayList<>(); + usedResources.set(collected); } + collected.addAll(resources); + original.call(languageName, resources, destinationMap); } /** @@ -51,7 +46,8 @@ public class ClientLanguageMixin { * @reason figure out which keys are dynamically loaded and which are injected by mixins */ @ModifyArg(method = "loadFrom", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/language/ClientLanguage;(Ljava/util/Map;Z)V"), index = 0) - private static Map modifyLanguageMap(Map storage) { - return DynamicLanguageMap.forStorage(Map.copyOf(storage)); + private static Map modifyLanguageMap(Map storage, @Share("usedResources") LocalRef> usedResources) { + List collected = Objects.requireNonNullElse(usedResources.get(), List.of()); + return DynamicLanguageMap.forVanillaData(storage, collected); } } diff --git a/src/main/java/org/embeddedt/modernfix/dynamiclanguages/DynamicLanguageMap.java b/src/main/java/org/embeddedt/modernfix/dynamiclanguages/DynamicLanguageMap.java index a3471627..9e6110dc 100644 --- a/src/main/java/org/embeddedt/modernfix/dynamiclanguages/DynamicLanguageMap.java +++ b/src/main/java/org/embeddedt/modernfix/dynamiclanguages/DynamicLanguageMap.java @@ -10,10 +10,28 @@ import net.minecraft.server.packs.resources.Resource; import org.embeddedt.modernfix.ModernFix; import java.io.IOException; +import java.util.HashMap; +import java.util.List; import java.util.Map; public class DynamicLanguageMap { - public static Map forStorage(Map storage) { + private static Map createStorage(Map rawLanguageContents, List languageResources) { + Map storage = new HashMap<>(rawLanguageContents); + for (var resource : languageResources) { + try (var stream = resource.open()) { + Language.loadFromJson(stream, (key, value) -> { + if (value != null && value.equals(storage.get(key))) { + storage.put(key, resource); + } + }); + } catch (Exception ignored) { + } + } + return Map.copyOf(storage); + } + + public static Map forVanillaData(Map rawLanguageContents, List languageResources) { + Map storage = createStorage(rawLanguageContents, languageResources); LoadingCache> languageFileContents = CacheBuilder.newBuilder() .softValues() .build(new CacheLoader<>() {