From 9edce9ad91ae833219a2a33b839fb66ca19c6bad Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 6 Mar 2026 20:52:26 -0500 Subject: [PATCH] Dynamically load/unload Unihex font data --- .../GlyphProviderTypeMixin.java | 20 ++++ .../render/font/LazyGlyphProvider.java | 105 ++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 src/main/java/org/embeddedt/modernfix/common/mixin/perf/compress_unihex_font/GlyphProviderTypeMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/render/font/LazyGlyphProvider.java diff --git a/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compress_unihex_font/GlyphProviderTypeMixin.java b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compress_unihex_font/GlyphProviderTypeMixin.java new file mode 100644 index 00000000..bdb2db07 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compress_unihex_font/GlyphProviderTypeMixin.java @@ -0,0 +1,20 @@ +package org.embeddedt.modernfix.common.mixin.perf.compress_unihex_font; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.mojang.serialization.MapCodec; +import net.minecraft.client.gui.font.providers.GlyphProviderDefinition; +import net.minecraft.client.gui.font.providers.GlyphProviderType; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.render.font.LazyGlyphProvider; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(GlyphProviderType.class) +@ClientOnlyMixin +public class GlyphProviderTypeMixin { + @ModifyExpressionValue(method = "", at = @At(value = "FIELD", opcode = Opcodes.GETSTATIC, target = "Lnet/minecraft/client/gui/font/providers/UnihexProvider$Definition;CODEC:Lcom/mojang/serialization/MapCodec;")) + private static MapCodec lazyUnihex(MapCodec codec) { + return LazyGlyphProvider.wrap(codec); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/render/font/LazyGlyphProvider.java b/src/main/java/org/embeddedt/modernfix/render/font/LazyGlyphProvider.java new file mode 100644 index 00000000..74e49d6f --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/render/font/LazyGlyphProvider.java @@ -0,0 +1,105 @@ +package org.embeddedt.modernfix.render.font; + +import com.mojang.blaze3d.font.GlyphInfo; +import com.mojang.blaze3d.font.GlyphProvider; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.MapCodec; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import net.minecraft.client.gui.font.providers.GlyphProviderDefinition; +import net.minecraft.client.gui.font.providers.GlyphProviderType; +import net.minecraft.server.packs.resources.ResourceManager; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.util.function.Function; + +public class LazyGlyphProvider implements GlyphProvider { + private final GlyphProviderDefinition.Loader loader; + private final ResourceManager manager; + + private SoftReference innerProvider = new SoftReference<>(null); + + private IntSet supportedGlyphs; + + LazyGlyphProvider(GlyphProviderDefinition.Loader loader, ResourceManager manager) { + this.loader = loader; + this.manager = manager; + } + + @Override + public void close() { + // best effort + var prov = innerProvider.get(); + if (prov != null) { + prov.close(); + } + } + + private @Nullable GlyphProvider getGlyphProvider() { + GlyphProvider prov = innerProvider.get(); + if (prov == null) { + try { + prov = this.loader.load(this.manager); + } catch (IOException e) { + return null; + } + innerProvider = new SoftReference<>(prov); + } + return prov; + } + + @Override + public @Nullable GlyphInfo getGlyph(int character) { + var prov = getGlyphProvider(); + if (prov != null) { + return prov.getGlyph(character); + } else { + return null; + } + } + + @Override + public IntSet getSupportedGlyphs() { + if (supportedGlyphs == null) { + var prov = getGlyphProvider(); + if (prov != null) { + supportedGlyphs = new IntOpenHashSet(prov.getSupportedGlyphs()); + } else { + supportedGlyphs = IntSet.of(); + } + } + return supportedGlyphs; + } + + private static class Definition implements GlyphProviderDefinition { + private final GlyphProviderDefinition delegate; + + public Definition(GlyphProviderDefinition delegate) { + this.delegate = delegate; + } + + @Override + public GlyphProviderType type() { + return this.delegate.type(); + } + + @Override + public Either unpack() { + return this.delegate.unpack().mapBoth( + loader -> resourceManager -> new LazyGlyphProvider(loader, resourceManager), + Function.identity() + ); + } + + @SuppressWarnings("unchecked") + public T delegate() { + return (T)this.delegate; + } + } + + public static MapCodec wrap(MapCodec codec) { + return codec.xmap(Definition::new, Definition::delegate); + } +}