From afd0e8c39daf7ae1b887017b1ce14c7499920095 Mon Sep 17 00:00:00 2001 From: M4elstr0m <112475171+M4elstr0m@users.noreply.github.com> Date: Mon, 27 Jul 2026 00:01:53 +0200 Subject: [PATCH] feat: 1.21.1 Fabric port & new keybind feature * Port to Minecraft 1.21.1 (Fabric) - Update Gradle wrapper to 8.8 and Java target to 21 - Bump Architectury, Fabric Loader, and Fabric API versions - Update Cloth Config and ModMenu dependencies - Fix LiteralContents mapping and ClientTooltipEvent signature - Refactor MixinLanguageSelectScreen to avoid injection crashes - Disable Forge module in settings.gradle * feat: implement global toggleable translate keybind fix: resolve text rendering aspect ratio and missing text fallback logic --- build.gradle | 9 +++-- .../fabbe50/langsplit/common/ClothScreen.java | 1 + .../fabbe50/langsplit/common/LangUtils.java | 21 +++++++++-- .../fabbe50/langsplit/common/Langsplit.java | 15 ++++++-- .../fabbe50/langsplit/common/ModConfig.java | 5 +++ .../langsplit/common/TextRenderHelper.java | 22 ++++++------ .../langsplit/common/mixin/MixinFont.java | 4 ++- .../common/mixin/MixinGuiComponent.java | 4 ++- .../mixin/MixinLanguageSelectScreen.java | 36 ++++++++++++------- .../assets/langsplit/lang/en_us.json | 3 ++ .../main/resources/langsplit.accesswidener | 3 +- fabric/build.gradle | 8 ++--- gradle.properties | 17 +++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle | 1 - 15 files changed, 99 insertions(+), 52 deletions(-) diff --git a/build.gradle b/build.gradle index bb7bb23..b29c277 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "architectury-plugin" version "3.4-SNAPSHOT" - id "dev.architectury.loom" version "1.1-SNAPSHOT" apply false + id "dev.architectury.loom" version "1.7-SNAPSHOT" apply false } architectury { @@ -30,14 +30,13 @@ allprojects { group = rootProject.maven_group repositories { - repositories { - maven { url "https://maven.terraformersmc.com/releases" } - } + maven { url "https://api.modrinth.com/maven" } + maven { url "https://maven.terraformersmc.com/releases" } } tasks.withType(JavaCompile).configureEach { options.encoding = "UTF-8" - options.release = 17 + options.release = 21 } java { diff --git a/common/src/main/java/com/fabbe50/langsplit/common/ClothScreen.java b/common/src/main/java/com/fabbe50/langsplit/common/ClothScreen.java index f1b5444..1eb12de 100644 --- a/common/src/main/java/com/fabbe50/langsplit/common/ClothScreen.java +++ b/common/src/main/java/com/fabbe50/langsplit/common/ClothScreen.java @@ -48,6 +48,7 @@ public static Screen getConfigScreen(Screen parent) { })).setDefaultValue("en_us").setSelections(Lists.newArrayList(getLocalizedLanguagesFromGame().keySet())).setSaveConsumer(s -> ModConfig.language = s).setSuggestionMode(false).build()); general.addEntry(entryBuilder.startBooleanToggle(Component.translatable("text.langsplit.option.inline"), ModConfig.inline).setDefaultValue(false).setSaveConsumer(s -> ModConfig.inline = s).build()); + general.addEntry(entryBuilder.startBooleanToggle(Component.translatable("text.langsplit.option.requirekeybind"), ModConfig.requireKeybind).setDefaultValue(false).setSaveConsumer(s -> ModConfig.requireKeybind = s).build()); general.addEntry(entryBuilder.startBooleanToggle(Component.translatable("text.langsplit.option.translationbrackets"), ModConfig.translationBrackets).setDefaultValue(false).setSaveConsumer(s -> ModConfig.translationBrackets = s).build()); general.addEntry(entryBuilder.startBooleanToggle(Component.translatable("text.langsplit.option.blendcolor"), ModConfig.blendColor) .setDefaultValue(true) diff --git a/common/src/main/java/com/fabbe50/langsplit/common/LangUtils.java b/common/src/main/java/com/fabbe50/langsplit/common/LangUtils.java index 5574ff7..126c414 100644 --- a/common/src/main/java/com/fabbe50/langsplit/common/LangUtils.java +++ b/common/src/main/java/com/fabbe50/langsplit/common/LangUtils.java @@ -4,7 +4,7 @@ import net.minecraft.client.resources.language.ClientLanguage; import net.minecraft.locale.Language; import net.minecraft.network.chat.*; -import net.minecraft.network.chat.contents.LiteralContents; +import net.minecraft.network.chat.contents.PlainTextContents; import net.minecraft.network.chat.contents.TranslatableContents; import java.util.*; @@ -12,6 +12,21 @@ public class LangUtils { public static Component[] translate(Component component) { Component[] newLines = new Component[2]; + if (ModConfig.requireKeybind && net.minecraft.client.Minecraft.getInstance().getWindow() != null) { + long window = net.minecraft.client.Minecraft.getInstance().getWindow().getWindow(); + int keyCode = Langsplit.TRANSLATE_KEY.key.getValue(); + boolean isKeyDown = false; + if (Langsplit.TRANSLATE_KEY.key.getType() == com.mojang.blaze3d.platform.InputConstants.Type.MOUSE) { + isKeyDown = org.lwjgl.glfw.GLFW.glfwGetMouseButton(window, keyCode) == 1; + } else { + isKeyDown = com.mojang.blaze3d.platform.InputConstants.isKeyDown(window, keyCode); + } + if (!isKeyDown) { + newLines[0] = component; + newLines[1] = component; + return newLines; + } + } if (Langsplit.getClientLanguage() != null) { ComponentContents contents = component.getContents(); if (contents instanceof TranslatableContents translatable) { @@ -57,7 +72,7 @@ public static Object[][] translateArg(Object[] args) { public static Component[] translateSiblings(Component component) { Component original = Component.literal(""); Component translation = Component.literal(""); - if (component.getContents() instanceof LiteralContents literal) { + if (component.getContents() instanceof PlainTextContents literal) { original = combine(original, Component.literal(literal.text()), CombineType.DIRECT); translation = combine(translation, Component.literal(literal.text()), CombineType.DIRECT); } @@ -82,7 +97,7 @@ public static Component[] decodeSiblings(Component component) { if (temp.length == 2) { translation = combine(translation, temp[1], CombineType.DIRECT); } - } else if (contents instanceof LiteralContents literal) { + } else if (contents instanceof PlainTextContents literal) { original = combine(original, Component.literal(literal.text()), CombineType.DIRECT); translation = combine(translation, Component.literal(literal.text()), CombineType.DIRECT); } diff --git a/common/src/main/java/com/fabbe50/langsplit/common/Langsplit.java b/common/src/main/java/com/fabbe50/langsplit/common/Langsplit.java index 27fa102..0e8ca75 100644 --- a/common/src/main/java/com/fabbe50/langsplit/common/Langsplit.java +++ b/common/src/main/java/com/fabbe50/langsplit/common/Langsplit.java @@ -5,7 +5,7 @@ import dev.architectury.event.events.client.ClientTickEvent; import dev.architectury.event.events.client.ClientTooltipEvent; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.LanguageSelectScreen; + import net.minecraft.client.resources.language.ClientLanguage; import net.minecraft.client.resources.language.LanguageInfo; import net.minecraft.client.resources.language.LanguageManager; @@ -14,6 +14,9 @@ import net.minecraft.network.chat.Style; import net.minecraft.network.chat.TextColor; import net.minecraft.network.chat.contents.TranslatableContents; +import net.minecraft.client.KeyMapping; +import com.mojang.blaze3d.platform.InputConstants; +import dev.architectury.registry.client.keymappings.KeyMappingRegistry; import java.util.ArrayList; import java.util.Arrays; @@ -28,9 +31,17 @@ public class Langsplit { private static LanguageManager manager; private static boolean firstRun = true; + public static final KeyMapping TRANSLATE_KEY = new KeyMapping( + "key.langsplit.translate", + InputConstants.Type.KEYSYM, + InputConstants.KEY_LALT, + "category.langsplit.general" + ); + public static void register() { ModConfig.register(); handleTooltipEvent(); + KeyMappingRegistry.register(TRANSLATE_KEY); } public static void setupLanguage(String secondary) { @@ -78,7 +89,7 @@ public static boolean isLanguageLoaded() { } public static void handleTooltipEvent() { - ClientTooltipEvent.ITEM.register((stack, lines, flag) -> { + ClientTooltipEvent.ITEM.register((stack, lines, context, flag) -> { if (!isLanguageLoaded()) return; diff --git a/common/src/main/java/com/fabbe50/langsplit/common/ModConfig.java b/common/src/main/java/com/fabbe50/langsplit/common/ModConfig.java index 01ffd50..e1ef53c 100644 --- a/common/src/main/java/com/fabbe50/langsplit/common/ModConfig.java +++ b/common/src/main/java/com/fabbe50/langsplit/common/ModConfig.java @@ -19,6 +19,7 @@ public class ModConfig { public static boolean blendColor; public static float blendingRatio; public static int textColor; + public static boolean requireKeybind; public static Map textLocations = new HashMap<>(); public static void register() { @@ -46,6 +47,7 @@ public static void load(File file) { language = ((String) properties.computeIfAbsent("language", a -> "en_us")); inline = ((String) properties.computeIfAbsent("inline", a -> "false")).equalsIgnoreCase("true"); translationBrackets = ((String) properties.computeIfAbsent("translationBrackets", a -> "false")).equalsIgnoreCase("true"); + requireKeybind = ((String) properties.computeIfAbsent("requireKeybind", a -> "false")).equalsIgnoreCase("true"); blendColor = ((String) properties.computeIfAbsent("blendTextColor", a -> "true")).equalsIgnoreCase("true"); blendingRatio = Float.parseFloat((String) properties.computeIfAbsent("blendingRatio", a -> "0.35f")); { @@ -61,6 +63,7 @@ public static void load(File file) { language = "en_us"; inline = false; translationBrackets = false; + requireKeybind = false; blendColor = true; blendingRatio = 0.35f; textColor = 0x77ff77; @@ -80,6 +83,8 @@ public static void save(File file) throws IOException { fos.write("\n".getBytes()); fos.write(("translationBrackets=" + translationBrackets).getBytes()); fos.write("\n".getBytes()); + fos.write(("requireKeybind=" + requireKeybind).getBytes()); + fos.write("\n".getBytes()); fos.write(("blendTextColor=" + blendColor).getBytes()); fos.write("\n".getBytes()); fos.write(("blendingRatio=" + blendingRatio).getBytes()); diff --git a/common/src/main/java/com/fabbe50/langsplit/common/TextRenderHelper.java b/common/src/main/java/com/fabbe50/langsplit/common/TextRenderHelper.java index 0ed5b0d..5b485cd 100644 --- a/common/src/main/java/com/fabbe50/langsplit/common/TextRenderHelper.java +++ b/common/src/main/java/com/fabbe50/langsplit/common/TextRenderHelper.java @@ -54,42 +54,42 @@ public GuiPositions(float inputX, float inputY) { } public GuiPositions getCenteredTwoLinesOnButton(PoseStack poseStack, Component component, float originalTextWidth, float translationTextWidth) { - originalX = getPositionX(originalTextWidth); originalY = getPositionY(2, 0); - translationX = getPositionX(translationTextWidth); translationY = getPositionY(1, 2); - poseStack.scale(1, scaleHeightFactor, 1); + originalX = getPositionX(originalTextWidth); + translationX = getPositionX(translationTextWidth); + poseStack.scale(scaleHeightFactor, scaleHeightFactor, 1); applyOverrides(component); return this; } public GuiPositions getTwoLinesOnButton(PoseStack poseStack, Component component, float originalTextWidth, float translationTextWidth) { float maxWidth = Math.max(originalTextWidth, translationTextWidth); - originalX = getPositionX(originalTextWidth, maxWidth); originalY = getPositionY(2, 0); - translationX = getPositionX(translationTextWidth, maxWidth); translationY = getPositionY(1, 2); - poseStack.scale(1, scaleHeightFactor, 1); + originalX = getPositionX(originalTextWidth, maxWidth); + translationX = getPositionX(translationTextWidth, maxWidth); + poseStack.scale(scaleHeightFactor, scaleHeightFactor, 1); applyOverrides(component); return this; } public GuiPositions getTwoLinesWithinMaxHeight(PoseStack poseStack, Component component, float heightScale) { - originalX = inputX; + originalX = inputX * (1f / heightScale); originalY = getPositionY(2, 14, heightScale); - translationX = inputX; + translationX = inputX * (1f / heightScale); translationY = getPositionY(1, 18, heightScale); - poseStack.scale(1f, heightScale, 1f); + poseStack.scale(heightScale, heightScale, 1f); applyOverrides(component); return this; } private float getPositionX(float width) { - return inputX - (width / 2f); + return (inputX * (1f / scaleHeightFactor)) - (width / 2f); } private float getPositionX(float width, float maxWidth) { - return (inputX - (width / 2f)) + (maxWidth / 2f); + return (inputX * (1f / scaleHeightFactor)) - (width / 2f) + (maxWidth / 2f); } private float getPositionY(float line, int offset) { diff --git a/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinFont.java b/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinFont.java index ff2b865..cad91c1 100644 --- a/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinFont.java +++ b/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinFont.java @@ -68,11 +68,13 @@ private void injectDrawInBatch(Component component, float x, float y, int color, } drawInternal(translatedText, positions.getTranslationX(), positions.getTranslationY(), color, bl, poseStack.last().pose(), multiBufferSource, displayMode, j, k); } + } else { + cir.setReturnValue(drawInternal(component.getVisualOrderText(), x, y, color, bl, matrix4f, multiBufferSource, displayMode, j, k)); } } catch (NullPointerException ignored) {} poseStack.popPose(); } else { - cir.setReturnValue(drawInternal(component.getVisualOrderText(), x, y, color, bl, poseStack.last().pose(), multiBufferSource, displayMode, j, k)); + cir.setReturnValue(drawInternal(component.getVisualOrderText(), x, y, color, bl, matrix4f, multiBufferSource, displayMode, j, k)); } cir.cancel(); } diff --git a/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinGuiComponent.java b/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinGuiComponent.java index b2db872..e92fab7 100644 --- a/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinGuiComponent.java +++ b/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinGuiComponent.java @@ -71,11 +71,13 @@ private void injectDrawString(Font font, Component component, int x, int y, int TextRenderHelper.GuiPositions positions = new TextRenderHelper.GuiPositions(x, y).getTwoLinesOnButton(pose, component, font.width(originalText), font.width(translatedText)); TextRenderHelper.drawTwoLines(pose, bufferSource, font, component, components, positions.getOriginalX(), positions.getOriginalY(), positions.getTranslationX(), positions.getTranslationY(), color, shadow); } + } else { + cir.setReturnValue(font.drawInBatch(component.getVisualOrderText(), (float)x, (float)y, color, shadow, this.pose.last().pose(), this.bufferSource, Font.DisplayMode.NORMAL, 0, 15728880)); } } catch (NullPointerException ignored) {} pose.popPose(); } else { - font.drawInBatch(component.getVisualOrderText(), x, y, color, shadow, this.pose.last().pose(), this.bufferSource, Font.DisplayMode.NORMAL, 0, 15728880); + cir.setReturnValue(font.drawInBatch(component.getVisualOrderText(), (float)x, (float)y, color, shadow, this.pose.last().pose(), this.bufferSource, Font.DisplayMode.NORMAL, 0, 15728880)); } cir.cancel(); } diff --git a/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinLanguageSelectScreen.java b/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinLanguageSelectScreen.java index 7dcd942..521fa6b 100644 --- a/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinLanguageSelectScreen.java +++ b/common/src/main/java/com/fabbe50/langsplit/common/mixin/MixinLanguageSelectScreen.java @@ -3,27 +3,37 @@ import com.fabbe50.langsplit.common.ClothScreen; import net.minecraft.client.Options; import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.screens.LanguageSelectScreen; -import net.minecraft.client.gui.screens.OptionsSubScreen; +import net.minecraft.client.gui.screens.options.LanguageSelectScreen; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; 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; -@Mixin(LanguageSelectScreen.class) -public abstract class MixinLanguageSelectScreen extends OptionsSubScreen { - public MixinLanguageSelectScreen(Screen screen, Options options, Component component) { - super(screen, options, component); - } +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.client.gui.components.Renderable; +import net.minecraft.client.Minecraft; +import org.jetbrains.annotations.Nullable; + +@Mixin(Screen.class) +public abstract class MixinLanguageSelectScreen { + @Shadow @Nullable public Minecraft minecraft; + + @Shadow public int width; + + @Shadow protected abstract T addRenderableWidget(T widget); - @Inject(at = @At("HEAD"), method = "init") + @Inject(at = @At("TAIL"), method = "init()V") private void injectInit(CallbackInfo ci) { - this.addRenderableWidget(Button.builder(Component.translatable("text.langsplit.title"), button -> { - if (this.minecraft != null) { - this.minecraft.setScreen(ClothScreen.getConfigScreen(this)); - } - }).bounds(this.width - 160, 8, 150, 20).build()); + if ((Object) this instanceof LanguageSelectScreen) { + this.addRenderableWidget(Button.builder(Component.translatable("text.langsplit.title"), button -> { + if (this.minecraft != null) { + this.minecraft.setScreen(ClothScreen.getConfigScreen((Screen) (Object) this)); + } + }).bounds(this.width - 160, 8, 150, 20).build()); + } } } diff --git a/common/src/main/resources/assets/langsplit/lang/en_us.json b/common/src/main/resources/assets/langsplit/lang/en_us.json index c5cedbd..2b3726b 100644 --- a/common/src/main/resources/assets/langsplit/lang/en_us.json +++ b/common/src/main/resources/assets/langsplit/lang/en_us.json @@ -3,6 +3,7 @@ "text.langsplit.option.language": "Language", "text.langsplit.option.languages": "Languages", "text.langsplit.option.inline": "Inline Translation", + "text.langsplit.option.requirekeybind": "Require Translate Key", "text.langsplit.option.debugger": "Debugging Tools", "text.langsplit.option.translationbrackets": "Encase in Brackets", "text.langsplit.option.blendcolor": "Blend Color", @@ -15,6 +16,8 @@ "text.langsplit.option.blendingratio.desc2": "Lower for more of the original color and higher for more of the translation color.", "text.langsplit.option.color": "Translation Text Color", "text.langsplit.category.general": "General", + "key.langsplit.translate": "Hold to Translate", + "category.langsplit.general": "LangSplit", "text.langsplit.language.af_za": "Afrikaans (Suid-Afrika)", "text.langsplit.language.ar_sa": "Arabic", "text.langsplit.language.ast_es": "Asturian", diff --git a/common/src/main/resources/langsplit.accesswidener b/common/src/main/resources/langsplit.accesswidener index 13268c3..741cb38 100644 --- a/common/src/main/resources/langsplit.accesswidener +++ b/common/src/main/resources/langsplit.accesswidener @@ -1 +1,2 @@ -accessWidener v2 named \ No newline at end of file +accessWidener v2 named +accessible field net/minecraft/client/KeyMapping key Lcom/mojang/blaze3d/platform/InputConstants$Key; \ No newline at end of file diff --git a/fabric/build.gradle b/fabric/build.gradle index 9b786bc..8188ae6 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -28,7 +28,7 @@ dependencies { exclude(group: "net.fabricmc.fabric-api") } - modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { + modImplementation("maven.modrinth:modmenu:${rootProject.modmenu_version}") { transitive = false } @@ -53,18 +53,18 @@ shadowJar { exclude "architectury.common.json" configurations = [project.configurations.shadowCommon] - classifier "dev-shadow" + archiveClassifier.set("dev-shadow") } remapJar { injectAccessWidener = true; inputFile.set shadowJar.archiveFile; dependsOn(shadowJar) - classifier null + archiveClassifier.set((String) null) } jar { - classifier "dev" + archiveClassifier.set("dev") } sourcesJar { diff --git a/gradle.properties b/gradle.properties index 51fc588..9ec4ff0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,25 +1,24 @@ org.gradle.jvmargs=-Xmx6G org.gradle.daemon=false -org.gradle.java.home=/home/fabbe50/.jdks/corretto-17.0.6 maven_group=com.fabbe50 archives_base_name=langsplit -enabled_platforms=fabric,forge +enabled_platforms=fabric # mod versions -minecraft_version=1.20.1 -mod_version=2.3-beta +minecraft_version=1.21.1 +mod_version=2.3-beta-1.21.1 # architectury -architectury_version=9.0.8 +architectury_version=13.0.6 # fabric -fabric_loader_version=0.14.21 -fabric_api_version=0.84.0+1.20.1 -modmenu_version=7.1.0 +fabric_loader_version=0.16.2 +fabric_api_version=0.102.0+1.21.1 +modmenu_version=11.0.2 # forge forge_version=47.0.35 #plugins -cloth_config_version=11.0.99 \ No newline at end of file +cloth_config_version=15.0.130 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 41dfb87..0d18421 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index 275a779..9d12a35 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,5 @@ pluginManagement { include("common") include("fabric") -include("forge") rootProject.name = "langsplit" \ No newline at end of file