From 640eb9a446cc2ed1efbcb668c208567eec5f7f6a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 7 May 2026 23:20:59 +0000 Subject: [PATCH 1/5] feat: consolidate multi-version support with Stonecutter Replaces three separate branches (1.21, 1.21.6-1.21.11, 26.1) with a single codebase using the Stonecutter Gradle plugin. Version-specific code is handled via //? if mc >= "26.1" preprocessor guards throughout the Java source, while per-version gradle.properties live in versions/. - settings.gradle: adds Stonecutter 0.5.1 with three version targets - build.gradle: selects Yarn vs Mojang mappings, correct Fabric API modules, and Java version (21 vs 25) per target - All Java source files updated with inline preprocessor guards - fabric.mod.json / betterhud.mixins.json templated via processResources - CI matrix builds all three versions in parallel https://claude.ai/code/session_01PHDS24JTjD32guufn2q2sj --- .github/workflows/build.yml | 34 +++-- build.gradle | 120 +++++++++------ gradle.properties | 13 +- settings.gradle | 18 ++- src/main/java/dsns/betterhud/BetterHUD.java | 11 +- .../java/dsns/betterhud/BetterHUDGUI.java | 137 ++++++++++++------ .../mixin/MinecraftClientAccessor.java | 17 +++ src/main/java/dsns/betterhud/mods/Biome.java | 29 +++- .../java/dsns/betterhud/mods/Coordinates.java | 10 ++ src/main/java/dsns/betterhud/mods/FPS.java | 14 +- src/main/java/dsns/betterhud/mods/Facing.java | 27 +++- .../java/dsns/betterhud/mods/Momentum.java | 33 +++-- src/main/java/dsns/betterhud/mods/Ping.java | 32 +++- src/main/java/dsns/betterhud/mods/Time.java | 8 + .../java/dsns/betterhud/util/BaseMod.java | 8 + src/main/resources/betterhud.mixins.json | 6 +- src/main/resources/fabric.mod.json | 8 +- versions/1.21.9/gradle.properties | 8 + versions/1.21/gradle.properties | 8 + versions/26.1/gradle.properties | 7 + 20 files changed, 387 insertions(+), 161 deletions(-) create mode 100644 src/main/java/dsns/betterhud/mixin/MinecraftClientAccessor.java create mode 100644 versions/1.21.9/gradle.properties create mode 100644 versions/1.21/gradle.properties create mode 100644 versions/26.1/gradle.properties diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0c2a3f3..4dfd053 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,30 +1,34 @@ -# Automatically build the project and run any configured tests for every push -# and submitted pull request. This can help catch issues that only occur on -# certain platforms or Java versions, and provides a first line of defence -# against bad commits. - name: build on: [pull_request, push] jobs: build: runs-on: ubuntu-24.04 + strategy: + matrix: + include: + - mc: "1.21" + java: "21" + - mc: "1.21.9" + java: "21" + - mc: "26.1" + java: "25" steps: - name: checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v4 - name: validate gradle wrapper - uses: gradle/actions/wrapper-validation@v6 - - name: setup jdk - uses: actions/setup-java@v5 + uses: gradle/actions/wrapper-validation@v4 + - name: setup jdk ${{ matrix.java }} + uses: actions/setup-java@v4 with: - java-version: '25' + java-version: ${{ matrix.java }} distribution: 'microsoft' - name: make gradle wrapper executable run: chmod +x ./gradlew - - name: build - run: ./gradlew build + - name: build mc${{ matrix.mc }} + run: ./gradlew :versions:${{ matrix.mc }}:build - name: capture build artifacts - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@v4 with: - name: Artifacts - path: build/libs/ \ No newline at end of file + name: Artifacts-mc${{ matrix.mc }} + path: versions/${{ matrix.mc }}/build/libs/ diff --git a/build.gradle b/build.gradle index 41f2871..4809ad8 100644 --- a/build.gradle +++ b/build.gradle @@ -1,87 +1,119 @@ plugins { id 'net.fabricmc.fabric-loom' version "${loom_version}" + id 'dev.kikugie.stonecutter' id 'maven-publish' } -version = project.mod_version +def mc = stonecutter.current.version +def isOldMC = mc.startsWith("1.") // 1.21.x → Yarn mappings, Java 21 +def is121 = mc == "1.21" // Only 1.21 needs lifecycle-events as explicit module + +stonecutter.const "mc", mc + +version = "${project.mod_version}+mc${project.minecraft_version}" group = project.maven_group +base { + archivesName = project.archives_base_name +} + repositories { - // Add repositories to retrieve artifacts from in here. - // You should only use this when depending on other mods because - // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. - // See https://docs.gradle.org/current/userguide/declaring_repositories.html - // for more information about repositories. - - maven { url "https://maven.shedaniel.me/" } - maven { url "https://maven.terraformersmc.com/releases/" } + maven { url "https://maven.shedaniel.me/" } + maven { url "https://maven.terraformersmc.com/releases/" } } -configurations.configureEach { - if (name.toLowerCase().contains('annotationprocessor')) { - exclude group: 'org.ow2.asm' +if (!isOldMC) { + configurations.configureEach { + if (name.toLowerCase().contains('annotationprocessor')) { + exclude group: 'org.ow2.asm' + } } } +def javaVersion = isOldMC ? 21 : 25 + dependencies { - // To change the versions see the gradle.properties file minecraft "com.mojang:minecraft:${project.minecraft_version}" - - implementation "net.fabricmc:fabric-loader:${project.loader_version}" - - // Fabric API. This is technically optional, but you probably want it anyway. - implementation include(fabricApi.module("fabric-api-base", project.fabric_api_version)) - implementation include(fabricApi.module("fabric-rendering-v1", project.fabric_api_version)) - - implementation include("com.terraformersmc:modmenu:${project.modmenu_version}") - implementation include("me.shedaniel.cloth:cloth-config-fabric:${project.cloth_config_version}") { - exclude(group: "net.fabricmc.fabric-api") - } + + if (isOldMC) { + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + modImplementation include(fabricApi.module("fabric-api-base", project.fabric_version)) + modImplementation include(fabricApi.module("fabric-rendering-v1", project.fabric_version)) + if (is121) { + modImplementation include(fabricApi.module("fabric-lifecycle-events-v1", project.fabric_version)) + } + modImplementation include("com.terraformersmc:modmenu:${project.modmenu_version}") + modImplementation include("me.shedaniel.cloth:cloth-config-fabric:${project.cloth_config_version}") { + exclude(group: "net.fabricmc.fabric-api") + } + } else { + mappings loom.officialMojangMappings() + implementation "net.fabricmc:fabric-loader:${project.loader_version}" + implementation include(fabricApi.module("fabric-api-base", project.fabric_api_version)) + implementation include(fabricApi.module("fabric-rendering-v1", project.fabric_api_version)) + implementation include("com.terraformersmc:modmenu:${project.modmenu_version}") + implementation include("me.shedaniel.cloth:cloth-config-fabric:${project.cloth_config_version}") { + exclude(group: "net.fabricmc.fabric-api") + } + } } processResources { + def minecraftReq = isOldMC ? "~1.21" : ">=26" + def loaderReq = isOldMC ? ">=0.15.11" : ">=0.19" + def javaReq = isOldMC ? ">=21" : ">=25" + def javaCompat = isOldMC ? "JAVA_21" : "JAVA_25" + def mixinClasses = isOldMC ? '"MinecraftClientAccessor"' : '' + inputs.property "version", project.version + inputs.property "minecraftReq", minecraftReq + inputs.property "loaderReq", loaderReq + inputs.property "javaReq", javaReq + inputs.property "javaCompat", javaCompat + inputs.property "mixinClasses", mixinClasses filesMatching("fabric.mod.json") { - expand "version": inputs.properties.version + expand( + "version": project.version, + "minecraftReq": minecraftReq, + "loaderReq": loaderReq, + "javaReq": javaReq + ) + } + filesMatching("betterhud.mixins.json") { + expand( + "javaCompat": javaCompat, + "mixinClasses": mixinClasses + ) } } tasks.withType(JavaCompile).configureEach { - it.options.release = 25 + it.options.release = javaVersion } java { - // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task - // if it is present. - // If you remove this line, sources will not be generated. withSourcesJar() - - sourceCompatibility = JavaVersion.VERSION_25 - targetCompatibility = JavaVersion.VERSION_25 + sourceCompatibility = JavaVersion.toVersion(javaVersion) + targetCompatibility = JavaVersion.toVersion(javaVersion) } jar { inputs.property "projectName", project.name - from("LICENSE") { - rename { "${it}_${project.name}"} + rename { "${it}_${project.name}" } } } -// configure the maven publication +// Registers a root-level task that builds all versions sequentially +stonecutter.chiseled tasks.register("chiseledBuild", stonecutter.task(tasks.named("build"))) + publishing { publications { create("mavenJava", MavenPublication) { from components.java } } - - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. - repositories { - // Add repositories to publish to here. - // Notice: This block does NOT have the same function as the block in the top level. - // The repositories here will be used for publishing your artifact, not for - // retrieving dependencies. - } -} \ No newline at end of file + repositories {} +} diff --git a/gradle.properties b/gradle.properties index a7faad0..7a2b84f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,18 +5,7 @@ org.gradle.parallel=true # IntelliJ IDEA is not yet fully compatible with configuration cache, see: https://github.com/FabricMC/fabric-loom/issues/1349 org.gradle.configuration-cache=false -# Fabric Properties -# check these on https://fabricmc.net/develop -minecraft_version=26.1 -loader_version=0.19.2 +# Shared across all versions loom_version=1.16-SNAPSHOT - -# Mod Properties -mod_version=2.1.0 maven_group=dsns.betterhud archives_base_name=betterhud - -# Dependencies -fabric_api_version=0.145.1+26.1 -modmenu_version=18.0.0-alpha.8 -cloth_config_version=26.1.154 \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index bcb1c6e..c988771 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,10 +4,26 @@ pluginManagement { name = 'Fabric' url = 'https://maven.fabricmc.net/' } + maven { + name = 'Stonecutter' + url = 'https://maven.kikugie.dev/releases' + } mavenCentral() gradlePluginPortal() } } -// Should match your modid +plugins { + id 'dev.kikugie.stonecutter' version '0.5.1' +} + +stonecutter { + create(rootProject) { + vers("1.21", "1.21") + vers("1.21.9", "1.21.9") + vers("26.1", "26.1") + active "26.1" + } +} + rootProject.name = 'betterhud' diff --git a/src/main/java/dsns/betterhud/BetterHUD.java b/src/main/java/dsns/betterhud/BetterHUD.java index bbf7edb..2486c02 100644 --- a/src/main/java/dsns/betterhud/BetterHUD.java +++ b/src/main/java/dsns/betterhud/BetterHUD.java @@ -8,17 +8,18 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +//? if mc >= "26.1" { import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElementRegistry; import net.minecraft.resources.Identifier; +//?} else { +/*import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;*/ +//?} import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Environment(EnvType.CLIENT) public class BetterHUD implements ClientModInitializer { - // This logger is used to write text to the console and the log file. - // It is considered best practice to use your mod id as the logger's name. - // That way, it's clear which mod wrote info, warnings, and errors. public static final Logger LOGGER = LoggerFactory.getLogger("betterhud"); public static ArrayList mods = new ArrayList<>( @@ -39,10 +40,14 @@ public void onInitializeClient() { BetterHUDGUI betterHUDGUI = new BetterHUDGUI(); + //? if mc >= "26.1" { HudElementRegistry.addLast( Identifier.fromNamespaceAndPath("betterhud", "hud"), betterHUDGUI::onHudRender ); + //?} else { + /*HudRenderCallback.EVENT.register(betterHUDGUI);*/ + //?} ClientTickEvents.START_CLIENT_TICK.register(betterHUDGUI); } } diff --git a/src/main/java/dsns/betterhud/BetterHUDGUI.java b/src/main/java/dsns/betterhud/BetterHUDGUI.java index 7d2b187..5304072 100644 --- a/src/main/java/dsns/betterhud/BetterHUDGUI.java +++ b/src/main/java/dsns/betterhud/BetterHUDGUI.java @@ -6,11 +6,21 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +//? if mc >= "26.1" { import net.minecraft.client.DeltaTracker; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; - -public class BetterHUDGUI implements ClientTickEvents.StartTick { +//?} else { +/*import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.render.RenderTickCounter; +import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;*/ +//?} + +public class BetterHUDGUI + //? if mc >= "26.1" + implements ClientTickEvents.StartTick { + //?> /*implements HudRenderCallback, ClientTickEvents.StartTick {*/ public static int verticalPadding = 4; public static int horizontalPadding = 4; @@ -20,7 +30,12 @@ public class BetterHUDGUI implements ClientTickEvents.StartTick { public static int lineHeight = 1; + //? if mc >= "26.1" { private final Minecraft client = Minecraft.getInstance(); + //?} else { + /*private final MinecraftClient client = MinecraftClient.getInstance();*/ + //?} + private final List topLeftText = new ObjectArrayList<>(); private final List topRightText = new ObjectArrayList<>(); private final List bottomLeftList = new ObjectArrayList<>(); @@ -28,7 +43,11 @@ public class BetterHUDGUI implements ClientTickEvents.StartTick { private final List customPositionText = new ObjectArrayList<>(); @Override + //? if mc >= "26.1" { public void onStartTick(Minecraft client) { + //?} else { + /*public void onStartTick(MinecraftClient client) {*/ + //?} this.topLeftText.clear(); this.topRightText.clear(); this.bottomLeftList.clear(); @@ -67,12 +86,22 @@ public void onStartTick(Minecraft client) { } } + //? if mc >= "26.1" { public void onHudRender( GuiGraphicsExtractor drawContext, DeltaTracker tickCounter ) { if (client.getDebugOverlay().showDebugScreen()) return; if (client.options.hideGui) return; + //?} else { + /*@Override + public void onHudRender( + DrawContext drawContext, + RenderTickCounter tickCounter + ) { + if (client.getDebugHud().shouldShowDebugHud()) return; + if (client.options.hudHidden) return;*/ + //?} int x = horizontalMargin; int y = verticalMargin; @@ -80,47 +109,64 @@ public void onHudRender( for (CustomText text : topLeftText) { drawString(drawContext, text, x, y); - y += - (client.font.lineHeight - 1) + - (verticalPadding * 2) + - lineHeight; + //? if mc >= "26.1" { + y += (client.font.lineHeight - 1) + (verticalPadding * 2) + lineHeight; + //?} else { + /*y += (client.textRenderer.fontHeight - 1) + (verticalPadding * 2) + lineHeight;*/ + //?} } + //? if mc >= "26.1" { y = client.getWindow().getGuiScaledHeight() - verticalMargin; + //?} else { + /*y = client.getWindow().getScaledHeight() - verticalMargin;*/ + //?} for (CustomText text : bottomLeftList) { + //? if mc >= "26.1" { y -= (client.font.lineHeight - 1) + (verticalPadding * 2); + //?} else { + /*y -= (client.textRenderer.fontHeight - 1) + (verticalPadding * 2);*/ + //?} drawString(drawContext, text, x, y); y -= lineHeight; } y = verticalMargin; for (CustomText text : topRightText) { - int offset = - (client.font.width(text.text) - 1) + - (horizontalPadding * 2) + - horizontalMargin; + //? if mc >= "26.1" { + int offset = (client.font.width(text.text) - 1) + (horizontalPadding * 2) + horizontalMargin; x = client.getWindow().getGuiScaledWidth() - offset; + //?} else { + /*int offset = (client.textRenderer.getWidth(text.text) - 1) + (horizontalPadding * 2) + horizontalMargin; + x = client.getWindow().getScaledWidth() - offset;*/ + //?} drawString(drawContext, text, x, y); - y += - (client.font.lineHeight - 1) + - (verticalPadding * 2) + - lineHeight; + //? if mc >= "26.1" { + y += (client.font.lineHeight - 1) + (verticalPadding * 2) + lineHeight; + //?} else { + /*y += (client.textRenderer.fontHeight - 1) + (verticalPadding * 2) + lineHeight;*/ + //?} } + //? if mc >= "26.1" { y = client.getWindow().getGuiScaledHeight() - verticalMargin; + //?} else { + /*y = client.getWindow().getScaledHeight() - verticalMargin;*/ + //?} for (CustomText text : bottomRightText) { - int offset = - (client.font.width(text.text) - 1) + - (horizontalPadding * 2) + - horizontalMargin; + //? if mc >= "26.1" { + int offset = (client.font.width(text.text) - 1) + (horizontalPadding * 2) + horizontalMargin; x = client.getWindow().getGuiScaledWidth() - offset; - y -= (client.font.lineHeight - 1) + (verticalPadding * 2); + //?} else { + /*int offset = (client.textRenderer.getWidth(text.text) - 1) + (horizontalPadding * 2) + horizontalMargin; + x = client.getWindow().getScaledWidth() - offset; + y -= (client.textRenderer.fontHeight - 1) + (verticalPadding * 2);*/ + //?} drawString(drawContext, text, x, y); - y -= lineHeight; } @@ -128,14 +174,13 @@ public void onHudRender( float xPercent = text.customX / 100.0f; float yPercent = text.customY / 100.0f; - int maxX = - client.getWindow().getGuiScaledWidth() - - (horizontalPadding * 2) - - (client.font.width(text.text) - 1); - int maxY = - client.getWindow().getGuiScaledHeight() - - (verticalPadding * 2) - - (client.font.lineHeight - 1); + //? if mc >= "26.1" { + int maxX = client.getWindow().getGuiScaledWidth() - (horizontalPadding * 2) - (client.font.width(text.text) - 1); + int maxY = client.getWindow().getGuiScaledHeight() - (verticalPadding * 2) - (client.font.lineHeight - 1); + //?} else { + /*int maxX = client.getWindow().getScaledWidth() - (horizontalPadding * 2) - (client.textRenderer.getWidth(text.text) - 1); + int maxY = client.getWindow().getScaledHeight() - (verticalPadding * 2) - (client.textRenderer.fontHeight - 1);*/ + //?} int scaledX = (int) (xPercent * maxX); int scaledY = (int) (yPercent * maxY); @@ -144,29 +189,25 @@ public void onHudRender( } } - private void drawString( - GuiGraphicsExtractor drawContext, - CustomText text, - int x, - int y - ) { + //? if mc >= "26.1" { + private void drawString(GuiGraphicsExtractor drawContext, CustomText text, int x, int y) { drawContext.fill( - x, - y, - x + - (client.font.width(text.text) - 1) + - (horizontalPadding * 2), + x, y, + x + (client.font.width(text.text) - 1) + (horizontalPadding * 2), y + (client.font.lineHeight - 1) + (verticalPadding * 2), text.backgroundColor ); - - drawContext.text( - client.font, - text.text, - x + horizontalPadding, - y + verticalPadding, - text.color, - true - ); + drawContext.text(client.font, text.text, x + horizontalPadding, y + verticalPadding, text.color, true); } + //?} else { + /*private void drawString(DrawContext drawContext, CustomText text, int x, int y) { + drawContext.fill( + x, y, + x + (client.textRenderer.getWidth(text.text) - 1) + (horizontalPadding * 2), + y + (client.textRenderer.fontHeight - 1) + (verticalPadding * 2), + text.backgroundColor + ); + drawContext.drawText(client.textRenderer, text.text, x + horizontalPadding, y + verticalPadding, text.color, true); + }*/ + //?} } diff --git a/src/main/java/dsns/betterhud/mixin/MinecraftClientAccessor.java b/src/main/java/dsns/betterhud/mixin/MinecraftClientAccessor.java new file mode 100644 index 0000000..41c42bc --- /dev/null +++ b/src/main/java/dsns/betterhud/mixin/MinecraftClientAccessor.java @@ -0,0 +1,17 @@ +package dsns.betterhud.mixin; + +//? if mc >= "26.1" { +public interface MinecraftClientAccessor {} +//?} else { +/*import net.minecraft.client.MinecraftClient; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MinecraftClient.class) +public interface MinecraftClientAccessor { + @Accessor("currentFps") + static int getCurrentFPS() { + return 0; + } +}*/ +//?} diff --git a/src/main/java/dsns/betterhud/mods/Biome.java b/src/main/java/dsns/betterhud/mods/Biome.java index 73289be..896e82c 100644 --- a/src/main/java/dsns/betterhud/mods/Biome.java +++ b/src/main/java/dsns/betterhud/mods/Biome.java @@ -5,9 +5,16 @@ import dsns.betterhud.util.ModSettings; import java.util.LinkedHashMap; import java.util.Map; +//? if mc >= "26.1" { import net.minecraft.client.Minecraft; import net.minecraft.core.Holder; import net.minecraft.world.entity.player.Player; +//?} else { +/*import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.registry.RegistryKey; +import java.util.Optional;*/ +//?} public class Biome implements BaseMod { @@ -24,6 +31,7 @@ public ModSettings getModSettings() { } @Override + //? if mc >= "26.1" { public CustomText onStartTick(Minecraft client) { Player player = client.player; @@ -35,6 +43,20 @@ public CustomText onStartTick(Minecraft client) { if (!biome.unwrapKey().isPresent()) return null; String biomeString = formatSnakeCase(biome.getRegisteredName().replace("minecraft:", "")); + //?} else { + /*public CustomText onStartTick(MinecraftClient client) { + PlayerEntity player = client.player; + + if (player == null) return null; + + // have to specify this because name of class is Biome + Optional> biome = + client.world.getBiome(player.getBlockPos()).getKey(); + + if (!biome.isPresent()) return null; + + String biomeString = formatSnakeCase(biome.get().getValue().getPath());*/ + //?} // used to maintain order when iterating LinkedHashMap biomeColors = new LinkedHashMap<>(); @@ -70,7 +92,7 @@ public CustomText onStartTick(Minecraft client) { biomeColors.put("Beach", 0xffffd700); biomeColors.put("Hills", 0xff2e8b57); - int color = 0xffffffff; // default color + int color = 0xffffffff; for (Map.Entry entry : biomeColors.entrySet()) { if (biomeString.contains(entry.getKey())) { @@ -83,17 +105,12 @@ public CustomText onStartTick(Minecraft client) { } public String formatSnakeCase(String biomeName) { - // Split the string by underscores String[] words = biomeName.split("_"); - - // Capitalize each word for (int i = 0; i < words.length; i++) { words[i] = words[i].substring(0, 1).toUpperCase() + words[i].substring(1).toLowerCase(); } - - // Join the words with spaces return String.join(" ", words); } } diff --git a/src/main/java/dsns/betterhud/mods/Coordinates.java b/src/main/java/dsns/betterhud/mods/Coordinates.java index 14c0cb1..7c8cc63 100644 --- a/src/main/java/dsns/betterhud/mods/Coordinates.java +++ b/src/main/java/dsns/betterhud/mods/Coordinates.java @@ -8,8 +8,13 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +//? if mc >= "26.1" { import net.minecraft.client.Minecraft; import net.minecraft.world.entity.player.Player; +//?} else { +/*import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.player.PlayerEntity;*/ +//?} class CoordinatesSettings extends ModSettings { @@ -55,8 +60,13 @@ public ModSettings getModSettings() { } @Override + //? if mc >= "26.1" { public CustomText onStartTick(Minecraft client) { Player player = client.player; + //?} else { + /*public CustomText onStartTick(MinecraftClient client) { + PlayerEntity player = client.player;*/ + //?} if (player == null) return null; diff --git a/src/main/java/dsns/betterhud/mods/FPS.java b/src/main/java/dsns/betterhud/mods/FPS.java index b509d2a..7367eae 100644 --- a/src/main/java/dsns/betterhud/mods/FPS.java +++ b/src/main/java/dsns/betterhud/mods/FPS.java @@ -1,9 +1,15 @@ package dsns.betterhud.mods; +//? if mc < "26.1" +/*import dsns.betterhud.mixin.MinecraftClientAccessor;*/ import dsns.betterhud.util.BaseMod; import dsns.betterhud.util.CustomText; import dsns.betterhud.util.ModSettings; +//? if mc >= "26.1" { import net.minecraft.client.Minecraft; +//?} else { +/*import net.minecraft.client.MinecraftClient;*/ +//?} public class FPS implements BaseMod { @@ -20,9 +26,15 @@ public ModSettings getModSettings() { } @Override + //? if mc >= "26.1" { public CustomText onStartTick(Minecraft client) { int currentFPS = client.getFps(); - return new CustomText(currentFPS + " FPS", getModSettings()); } + //?} else { + /*public CustomText onStartTick(MinecraftClient client) { + int currentFPS = MinecraftClientAccessor.getCurrentFPS(); + return new CustomText(currentFPS + " FPS", getModSettings()); + }*/ + //?} } diff --git a/src/main/java/dsns/betterhud/mods/Facing.java b/src/main/java/dsns/betterhud/mods/Facing.java index 9541993..92fc30c 100644 --- a/src/main/java/dsns/betterhud/mods/Facing.java +++ b/src/main/java/dsns/betterhud/mods/Facing.java @@ -3,14 +3,17 @@ import dsns.betterhud.util.BaseMod; import dsns.betterhud.util.CustomText; import dsns.betterhud.util.ModSettings; +//? if mc >= "26.1" { import net.minecraft.client.Minecraft; import net.minecraft.world.entity.player.Player; +//?} else { +/*import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.player.PlayerEntity;*/ +//?} public class Facing implements BaseMod { - private static final ModSettings SETTINGS = new ModSettings( - "top-right" - ); + private static final ModSettings SETTINGS = new ModSettings("top-right"); @Override public String getModID() { @@ -23,6 +26,7 @@ public ModSettings getModSettings() { } @Override + //? if mc >= "26.1" { public CustomText onStartTick(Minecraft client) { Player player = client.player; @@ -33,19 +37,26 @@ public CustomText onStartTick(Minecraft client) { getModSettings() ); } + //?} else { + /*public CustomText onStartTick(MinecraftClient client) { + PlayerEntity player = client.player; + + if (player == null || player.getHorizontalFacing() == null) return null; + + return new CustomText( + formatSnakeCase(player.getHorizontalFacing().name()), + getModSettings() + ); + }*/ + //?} public String formatSnakeCase(String biomeName) { - // Split the string by underscores String[] words = biomeName.split("_"); - - // Capitalize each word for (int i = 0; i < words.length; i++) { words[i] = words[i].substring(0, 1).toUpperCase() + words[i].substring(1).toLowerCase(); } - - // Join the words with spaces return String.join(" ", words); } } diff --git a/src/main/java/dsns/betterhud/mods/Momentum.java b/src/main/java/dsns/betterhud/mods/Momentum.java index a8355e7..b50f0c2 100644 --- a/src/main/java/dsns/betterhud/mods/Momentum.java +++ b/src/main/java/dsns/betterhud/mods/Momentum.java @@ -3,9 +3,15 @@ import dsns.betterhud.util.BaseMod; import dsns.betterhud.util.CustomText; import dsns.betterhud.util.ModSettings; +//? if mc >= "26.1" { import net.minecraft.client.Minecraft; import net.minecraft.util.Mth; import net.minecraft.world.entity.player.Player; +//?} else { +/*import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.math.MathHelper;*/ +//?} public class Momentum implements BaseMod { @@ -22,6 +28,7 @@ public ModSettings getModSettings() { } @Override + //? if mc >= "26.1" { public CustomText onStartTick(Minecraft client) { Player player = client.player; @@ -30,14 +37,22 @@ public CustomText onStartTick(Minecraft client) { double travelledX = player.getX() - player.xOld; double travelledZ = player.getZ() - player.zOld; double currentSpeed = - Mth.sqrt( - (float) (travelledX * travelledX + travelledZ * travelledZ) - ) / - 0.05F; - - return new CustomText( - String.format("%.2f m/s", currentSpeed), - getModSettings() - ); + Mth.sqrt((float) (travelledX * travelledX + travelledZ * travelledZ)) / 0.05F; + + return new CustomText(String.format("%.2f m/s", currentSpeed), getModSettings()); } + //?} else { + /*public CustomText onStartTick(MinecraftClient client) { + PlayerEntity player = client.player; + + if (player == null) return null; + + double travelledX = player.getX() - player.lastRenderX; + double travelledZ = player.getZ() - player.lastRenderZ; + double currentSpeed = + MathHelper.sqrt((float) (travelledX * travelledX + travelledZ * travelledZ)) / 0.05F; + + return new CustomText(String.format("%.2f m/s", currentSpeed), getModSettings()); + }*/ + //?} } diff --git a/src/main/java/dsns/betterhud/mods/Ping.java b/src/main/java/dsns/betterhud/mods/Ping.java index 078e02e..58e42e9 100644 --- a/src/main/java/dsns/betterhud/mods/Ping.java +++ b/src/main/java/dsns/betterhud/mods/Ping.java @@ -3,8 +3,13 @@ import dsns.betterhud.util.BaseMod; import dsns.betterhud.util.CustomText; import dsns.betterhud.util.ModSettings; +//? if mc >= "26.1" { import net.minecraft.client.Minecraft; import net.minecraft.world.entity.player.Player; +//?} else { +/*import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.player.PlayerEntity;*/ +//?} public class Ping implements BaseMod { @@ -21,6 +26,7 @@ public ModSettings getModSettings() { } @Override + //? if mc >= "26.1" { public CustomText onStartTick(Minecraft client) { Player player = client.player; @@ -28,17 +34,29 @@ public CustomText onStartTick(Minecraft client) { player == null || player.getUUID() == null || client.getConnection() == null || - client.getConnection().getPlayerInfo(player.getUUID()) == - null + client.getConnection().getPlayerInfo(player.getUUID()) == null ) return null; return new CustomText( - client - .getConnection() - .getPlayerInfo(player.getUUID()) - .getLatency() + - " ms", + client.getConnection().getPlayerInfo(player.getUUID()).getLatency() + " ms", getModSettings() ); } + //?} else { + /*public CustomText onStartTick(MinecraftClient client) { + PlayerEntity player = client.player; + + if ( + player == null || + player.getUuid() == null || + client.getNetworkHandler() == null || + client.getNetworkHandler().getPlayerListEntry(player.getUuid()) == null + ) return null; + + return new CustomText( + client.getNetworkHandler().getPlayerListEntry(player.getUuid()).getLatency() + " ms", + getModSettings() + ); + }*/ + //?} } diff --git a/src/main/java/dsns/betterhud/mods/Time.java b/src/main/java/dsns/betterhud/mods/Time.java index 722145b..3049077 100644 --- a/src/main/java/dsns/betterhud/mods/Time.java +++ b/src/main/java/dsns/betterhud/mods/Time.java @@ -5,7 +5,11 @@ import dsns.betterhud.util.ModSettings; import java.time.LocalTime; import java.time.format.DateTimeFormatter; +//? if mc >= "26.1" { import net.minecraft.client.Minecraft; +//?} else { +/*import net.minecraft.client.MinecraftClient;*/ +//?} public class Time implements BaseMod { @@ -22,7 +26,11 @@ public ModSettings getModSettings() { } @Override + //? if mc >= "26.1" { public CustomText onStartTick(Minecraft client) { + //?} else { + /*public CustomText onStartTick(MinecraftClient client) {*/ + //?} LocalTime currentTime = LocalTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("h:mm a"); String formattedTime = currentTime.format(formatter); diff --git a/src/main/java/dsns/betterhud/util/BaseMod.java b/src/main/java/dsns/betterhud/util/BaseMod.java index 0d35bf4..cd5a705 100644 --- a/src/main/java/dsns/betterhud/util/BaseMod.java +++ b/src/main/java/dsns/betterhud/util/BaseMod.java @@ -1,11 +1,19 @@ package dsns.betterhud.util; +//? if mc >= "26.1" { import net.minecraft.client.Minecraft; +//?} else { +/*import net.minecraft.client.MinecraftClient;*/ +//?} public interface BaseMod { public String getModID(); public ModSettings getModSettings(); + //? if mc >= "26.1" { public CustomText onStartTick(Minecraft client); + //?} else { + /*public CustomText onStartTick(MinecraftClient client);*/ + //?} } diff --git a/src/main/resources/betterhud.mixins.json b/src/main/resources/betterhud.mixins.json index 370eecf..a171ed3 100644 --- a/src/main/resources/betterhud.mixins.json +++ b/src/main/resources/betterhud.mixins.json @@ -1,12 +1,12 @@ { "required": true, "package": "dsns.betterhud.mixin", - "compatibilityLevel": "JAVA_25", - "client": [], + "compatibilityLevel": "${javaCompat}", + "client": [${mixinClasses}], "injectors": { "defaultRequire": 1 }, "overwrites": { "requireAnnotations": true } -} \ No newline at end of file +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 31b270b..b116dc0 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -21,8 +21,8 @@ "betterhud.mixins.json" ], "depends": { - "fabricloader": ">=0.19", - "minecraft": ">=26", - "java": ">=25" + "fabricloader": "${loaderReq}", + "minecraft": "${minecraftReq}", + "java": "${javaReq}" } -} \ No newline at end of file +} diff --git a/versions/1.21.9/gradle.properties b/versions/1.21.9/gradle.properties new file mode 100644 index 0000000..d9be61c --- /dev/null +++ b/versions/1.21.9/gradle.properties @@ -0,0 +1,8 @@ +minecraft_version=1.21.9 +yarn_mappings=1.21.9+build.1 +loader_version=0.17.3 + +mod_version=2.0.2 +fabric_version=0.134.0+1.21.9 +modmenu_version=15.0.2 +cloth_config_version=19.0.147 diff --git a/versions/1.21/gradle.properties b/versions/1.21/gradle.properties new file mode 100644 index 0000000..6c24bc2 --- /dev/null +++ b/versions/1.21/gradle.properties @@ -0,0 +1,8 @@ +minecraft_version=1.21 +yarn_mappings=1.21+build.9 +loader_version=0.17.3 + +mod_version=2.0.2 +fabric_version=0.102.0+1.21 +modmenu_version=14.0.2 +cloth_config_version=19.0.147 diff --git a/versions/26.1/gradle.properties b/versions/26.1/gradle.properties new file mode 100644 index 0000000..e34a9fd --- /dev/null +++ b/versions/26.1/gradle.properties @@ -0,0 +1,7 @@ +minecraft_version=26.1 +loader_version=0.19.2 + +mod_version=2.1.0 +fabric_api_version=0.145.1+26.1 +modmenu_version=18.0.0-alpha.8 +cloth_config_version=26.1.154 From aa1729af5410fcc3f3a5f253b2a4e76d85fa1008 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 7 May 2026 23:24:08 +0000 Subject: [PATCH 2/5] fix: correct Stonecutter API usage and BetterHUDGUI preprocessor syntax - build.gradle: use project.minecraft_version instead of the non-existent stonecutter.current.version property - build.gradle: remove stonecutter.chiseled call that used incorrect task API and caused a configuration-phase crash - BetterHUDGUI.java: replace invalid //?> with proper //? if block for the class declaration implements clause https://claude.ai/code/session_01PHDS24JTjD32guufn2q2sj --- build.gradle | 5 +---- src/main/java/dsns/betterhud/BetterHUDGUI.java | 9 +++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 4809ad8..d6f98cc 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id 'maven-publish' } -def mc = stonecutter.current.version +def mc = project.minecraft_version def isOldMC = mc.startsWith("1.") // 1.21.x → Yarn mappings, Java 21 def is121 = mc == "1.21" // Only 1.21 needs lifecycle-events as explicit module @@ -106,9 +106,6 @@ jar { } } -// Registers a root-level task that builds all versions sequentially -stonecutter.chiseled tasks.register("chiseledBuild", stonecutter.task(tasks.named("build"))) - publishing { publications { create("mavenJava", MavenPublication) { diff --git a/src/main/java/dsns/betterhud/BetterHUDGUI.java b/src/main/java/dsns/betterhud/BetterHUDGUI.java index 5304072..6378244 100644 --- a/src/main/java/dsns/betterhud/BetterHUDGUI.java +++ b/src/main/java/dsns/betterhud/BetterHUDGUI.java @@ -17,10 +17,11 @@ import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;*/ //?} -public class BetterHUDGUI - //? if mc >= "26.1" - implements ClientTickEvents.StartTick { - //?> /*implements HudRenderCallback, ClientTickEvents.StartTick {*/ +//? if mc >= "26.1" { +public class BetterHUDGUI implements ClientTickEvents.StartTick { +//?} else { +/*public class BetterHUDGUI implements HudRenderCallback, ClientTickEvents.StartTick {*/ +//?} public static int verticalPadding = 4; public static int horizontalPadding = 4; From 9775e8ba34fd084016cbe26de9b01293e86d77bf Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 8 May 2026 00:01:57 +0000 Subject: [PATCH 3/5] fix: correct Stonecutter 0.5.1 settings API and CI task paths - Replace `active "26.1"` (invalid on TreeBuilder) with `vcsVersion = "26.1"` in settings.gradle - Fix CI task path from `:versions::build` to `::build` (Stonecutter registers versions without the `versions:` prefix) - Add auto-generated stonecutter.gradle controller file with chiseledBuild task registration https://claude.ai/code/session_01PHDS24JTjD32guufn2q2sj --- .github/workflows/build.yml | 2 +- gradlew | 0 settings.gradle | 2 +- stonecutter.gradle | 7 +++++++ 4 files changed, 9 insertions(+), 2 deletions(-) mode change 100644 => 100755 gradlew create mode 100644 stonecutter.gradle diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4dfd053..683c702 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: - name: make gradle wrapper executable run: chmod +x ./gradlew - name: build mc${{ matrix.mc }} - run: ./gradlew :versions:${{ matrix.mc }}:build + run: ./gradlew :${{ matrix.mc }}:build - name: capture build artifacts uses: actions/upload-artifact@v4 with: diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/settings.gradle b/settings.gradle index c988771..e1e4b5b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,7 +22,7 @@ stonecutter { vers("1.21", "1.21") vers("1.21.9", "1.21.9") vers("26.1", "26.1") - active "26.1" + vcsVersion = "26.1" } } diff --git a/stonecutter.gradle b/stonecutter.gradle new file mode 100644 index 0000000..993b847 --- /dev/null +++ b/stonecutter.gradle @@ -0,0 +1,7 @@ +plugins.apply "dev.kikugie.stonecutter" +stonecutter.active "26.1" /* [SC] DO NOT EDIT */ + +stonecutter.registerChiseled tasks.register("chiseledBuild", stonecutter.chiseled) { + setGroup "project" + ofTask "build" +} \ No newline at end of file From e3a3f4be24caefb113b1b74670b508ea9dc2af3b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 8 May 2026 00:16:03 +0000 Subject: [PATCH 4/5] debug: add fail-fast=false and --stacktrace to diagnose CI failures All three MC version builds fail at different times (~25-30s), suggesting a common configuration-phase failure. With fail-fast disabled and stacktrace enabled, we can see the actual error for each version simultaneously. https://claude.ai/code/session_01PHDS24JTjD32guufn2q2sj --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 683c702..f37a7ec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,6 +5,7 @@ jobs: build: runs-on: ubuntu-24.04 strategy: + fail-fast: false matrix: include: - mc: "1.21" @@ -26,7 +27,7 @@ jobs: - name: make gradle wrapper executable run: chmod +x ./gradlew - name: build mc${{ matrix.mc }} - run: ./gradlew :${{ matrix.mc }}:build + run: ./gradlew :${{ matrix.mc }}:build --stacktrace - name: capture build artifacts uses: actions/upload-artifact@v4 with: From b3e9fd0828cbe0f2af5405d80dc12b26ab672114 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 8 May 2026 00:23:20 +0000 Subject: [PATCH 5/5] fix: remove duplicate Stonecutter plugin application and use correct dependency API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit StonecutterController auto-applies the Stonecutter plugin to each version subproject via pluginManager. Having `id 'dev.kikugie.stonecutter'` in build.gradle caused a duplicate extension registration error crashing all three version builds during configuration phase. Also fixes the mc version constant: `stonecutter.const "mc", mc` was calling `const(String, boolean)` (Groovy coerces String → true), which sets a useless boolean flag. The correct API for preprocessor version comparisons like `//? if mc >= "26.1"` is `stonecutter.dependency "mc", mc` which registers `mc` as a semantic version dependency. https://claude.ai/code/session_01PHDS24JTjD32guufn2q2sj --- build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d6f98cc..674f6d7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,5 @@ plugins { id 'net.fabricmc.fabric-loom' version "${loom_version}" - id 'dev.kikugie.stonecutter' id 'maven-publish' } @@ -8,7 +7,7 @@ def mc = project.minecraft_version def isOldMC = mc.startsWith("1.") // 1.21.x → Yarn mappings, Java 21 def is121 = mc == "1.21" // Only 1.21 needs lifecycle-events as explicit module -stonecutter.const "mc", mc +stonecutter.dependency "mc", mc version = "${project.mod_version}+mc${project.minecraft_version}" group = project.maven_group