Implement dynamic language loading
This commit is contained in:
parent
5a9c49f8d4
commit
db13f39b30
|
|
@ -0,0 +1,57 @@
|
|||
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 net.minecraft.client.resources.language.ClientLanguage;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.dynamiclanguages.DynamicLanguageMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Modifies the language system to load/unload the contents of language entries based on GC pressure.
|
||||
*/
|
||||
@Mixin(ClientLanguage.class)
|
||||
@ClientOnlyMixin
|
||||
public class ClientLanguageMixin {
|
||||
private static final ThreadLocal<Boolean> 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
|
||||
*/
|
||||
@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<String, ?> changeSemanticsOfConsumer(BiConsumer<String, ?> consumer, @Local(ordinal = 0, argsOnly = true) Map<String, Object> 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<Resource> resources, Map<String, String> destinationMap, Operation<Void> original) {
|
||||
MFIX_MODIFY_APPEND_SEMANTICS.set(true);
|
||||
try {
|
||||
original.call(languageName, resources, destinationMap);
|
||||
} finally {
|
||||
MFIX_MODIFY_APPEND_SEMANTICS.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @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;<init>(Ljava/util/Map;Z)V"), index = 0)
|
||||
private static Map<String, String> modifyLanguageMap(Map<String, ?> storage) {
|
||||
return DynamicLanguageMap.forStorage(Map.copyOf(storage));
|
||||
}
|
||||
}
|
||||
|
|
@ -194,6 +194,7 @@ public class ModernFixEarlyConfig {
|
|||
.putConditionally(() -> !isFabric, "mixin.feature.registry_event_progress", false)
|
||||
// Beta (promote on next release)
|
||||
.put("mixin.perf.compact_entity_models", false)
|
||||
.put("mixin.perf.dynamic_languages", false)
|
||||
.put("mixin.perf.faster_capabilities.bytecode_analysis", false)
|
||||
.put("mixin.perf.ingredient_item_deduplication", false)
|
||||
// END
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
package org.embeddedt.modernfix.dynamiclanguages;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.Maps;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.locale.Language;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class DynamicLanguageMap {
|
||||
public static Map<String, String> forStorage(Map<String, ?> storage) {
|
||||
LoadingCache<Resource, Map<String, String>> languageFileContents = CacheBuilder.newBuilder()
|
||||
.softValues()
|
||||
.build(new CacheLoader<>() {
|
||||
@Override
|
||||
public Map<String, String> load(Resource resource) throws Exception {
|
||||
Map<String, String> data = new Object2ObjectOpenHashMap<>();
|
||||
try (var stream = resource.open()) {
|
||||
Language.loadFromJson(stream, data::put);
|
||||
} catch (IOException e) {
|
||||
ModernFix.LOGGER.error("Error loading language data from {}", resource.sourcePackId(), e);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
});
|
||||
return Maps.asMap(storage.keySet(), k -> {
|
||||
var value = storage.get(k);
|
||||
if (value instanceof Resource r) {
|
||||
return languageFileContents.getUnchecked(r).getOrDefault(k, "");
|
||||
} else if (value instanceof String s) {
|
||||
return s;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user