-
Notifications
You must be signed in to change notification settings - Fork 0
Port Stats Ring from ChatTriggers module to 1.21.10 Fabric mod #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add ModConfig with Properties-based serialization - Add ConfigScreen using Cloth Config API - Add StatsRingRenderer porting main.js logic - Update StatsRing entry point with /ring command - Add Cloth Config dependency Co-authored-by: bubner <81782264+bubner@users.noreply.github.com>
Co-authored-by: bubner <81782264+bubner@users.noreply.github.com>
- Add deprecation comment for HudRenderCallback - Fix parseStat to match original comma-stripping parseInt logic - Use ChatFormatting enum instead of raw formatting codes in title Co-authored-by: bubner <81782264+bubner@users.noreply.github.com>
| if (config.getBackingImage()) { | ||
| int ringX = xCenter - RING_SIZE / 2; | ||
| int ringY = yCenter - RING_SIZE / 2; | ||
| graphics.blit(RenderType::guiTextured, RING_TEXTURE, ringX, ringY, 0, 0, RING_SIZE, RING_SIZE, RING_SIZE, RING_SIZE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
com.mojang.blaze3d.pipeline.RenderPipeline is not a functional interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in bf31df0 — replaced graphics.blit(RenderType::guiTextured, ...) with graphics.blitSprite(RenderPipelines.GUI_TEXTURED, RING_TEXTURE, ringX, ringY, RING_SIZE, RING_SIZE).
| String hpText = Math.round(healthPercent) + "%"; | ||
| int hpTextX = Math.round(healthPercent) >= 100 ? xCenter - 32 : xCenter - 28; | ||
| int hpTextY = yCenter - Math.round(mc.font.lineHeight * scale / 2f); | ||
| graphics.pose().pushPose(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pose returns public org.joml.Matrix3x2fStack pose(), hence pushPose() does not exist and dest argument of pose().scale(scale, scale, dest) must be Matrix3x2f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in bf31df0 — now uses Matrix3x2fStack pose = graphics.pose() with pushMatrix()/popMatrix() and translate()/scale() matching the 1.21.10 API, as seen in other mods like MEH's SkyBlockPrettyHudHudElement.
| ClientReceiveMessageEvents.GAME.register(this::onGameMessage); | ||
| ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> resetState()); | ||
| ClientTickEvents.END_CLIENT_TICK.register(client -> onTick()); | ||
| HudRenderCallback.EVENT.register(this::onHudRender); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can HudElementRegistry be used instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in bf31df0 — StatsRingRenderer now implements HudElement and registers via HudElementRegistry.attachElementAfter(VanillaHudElements.CROSSHAIR, ...).
| String msg = message.getString(); | ||
|
|
||
| // Extract health information | ||
| if (msg.contains("\u2764")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont use unicode escape sequences
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in bf31df0 — replaced \u2764 and \u270E with literal ❤ and ✎.
| // --- Utility methods --- | ||
|
|
||
| private static boolean isInSkyBlock() { | ||
| Minecraft mc = Minecraft.getInstance(); | ||
| if (mc.level == null) return false; | ||
| Scoreboard scoreboard = mc.level.getScoreboard(); | ||
| Objective objective = scoreboard.getDisplayObjective(DisplaySlot.SIDEBAR); | ||
| if (objective == null) return false; | ||
| return objective.getDisplayName().getString().contains("SKYBLOCK"); | ||
| } | ||
|
|
||
| private static float parseStat(String stat) { | ||
| try { | ||
| return Integer.parseInt(stat.replaceAll(",", "").trim()); | ||
| } catch (NumberFormatException e) { | ||
| return Float.NaN; | ||
| } | ||
| } | ||
|
|
||
| private static float lerp(float start, float end, float t) { | ||
| return start + (end - start) * t; | ||
| } | ||
|
|
||
| private static int argb(int a, int r, int g, int b) { | ||
| return (a << 24) | (r << 16) | (g << 8) | b; | ||
| } | ||
|
|
||
| private static int darkenRgb(int colour, float factor) { | ||
| int r = Math.round(((colour >> 16) & 0xFF) * factor); | ||
| int g = Math.round(((colour >> 8) & 0xFF) * factor); | ||
| int b = Math.round((colour & 0xFF) * factor); | ||
| return argb(255, r, g, b); | ||
| } | ||
|
|
||
| private static void drawBoldText(GuiGraphics graphics, Minecraft mc, String text, int x, int y, int color) { | ||
| Component component = Component.literal(text).withStyle(Style.EMPTY.withBold(true)); | ||
| graphics.drawString(mc.font, component, x, y, color, true); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move to a separate Util.java class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in bf31df0 — moved isInSkyBlock, parseStat, lerp, argb, darkenRgb, and drawBoldText to Util.java. Imported via static me.bubner.statsring.Util.*.
| private float maxMana = Float.NaN; | ||
|
|
||
| // 0 = mana read OK, 1 = mana frozen/missing, 2 = NOT ENOUGH MANA | ||
| private int manaReadStatus = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use an enum instead of integer and comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in bf31df0 — added ManaReadStatus enum with OK, FROZEN, NOT_ENOUGH_MANA values.
| })) | ||
| ); | ||
|
|
||
| LOGGER.info("StatsRing initialised"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
init message remove, use local var for config final ModConfig CONFIG = new ModConfig(); instead of instance field
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in bf31df0 — removed LOGGER.info("StatsRing initialised"), changed CONFIG from a public static final field to a local final ModConfig config variable in onInitializeClient(), and passed it as a parameter to ConfigScreen.create().
| * StatsRing - Display Health and Mana in a ring around the crosshair on Hypixel SkyBlock. | ||
| * Ported from a 1.8.9 ChatTriggers module into a 1.21.10 Fabric mod. | ||
| * | ||
| * @author Lucas Bubner, 2023-2025 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
years only 2023, should be @author Lucas Bubner, 2023 (Original CT module)
do this for all author tags
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in bf31df0 — all @author tags updated to @author Lucas Bubner, 2023 (Original CT module).
- Fix blit call: use graphics.blitSprite(RenderPipelines.GUI_TEXTURED, ...) instead of RenderType::guiTextured - Fix pose transforms: use Matrix3x2fStack with pushMatrix/popMatrix/translate/scale - Use HudElementRegistry.attachElementAfter(CROSSHAIR) instead of deprecated HudRenderCallback - Replace unicode escape sequences with actual characters (❤, ✎) - Extract utility methods into Util.java - Add ManaReadStatus enum to replace integer + comment - Use local var for config in StatsRing instead of static field - Remove init log message - Fix all @author tags to "2023 (Original CT module)" - Pass config as parameter to ConfigScreen.create() - Use Identifier instead of ResourceLocation for 1.21.10 Mojang mappings Co-authored-by: bubner <81782264+bubner@users.noreply.github.com>
| if (config.getBackingImage()) { | ||
| int ringX = xCenter - RING_SIZE / 2; | ||
| int ringY = yCenter - RING_SIZE / 2; | ||
| graphics.blitSprite(RenderPipelines.GUI_TEXTURED, RING_TEXTURE, ringX, ringY, RING_SIZE, RING_SIZE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
graphics.blitSprite does not exist on type GuiGraphics with official mojang mappings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in ba317eb — replaced graphics.blitSprite(RenderPipelines.GUI_TEXTURED, ...) with graphics.blit(RenderPipelines.GUI_TEXTURED, RING_TEXTURE, ringX, ringY, 0, 0, RING_SIZE, RING_SIZE, RING_SIZE, RING_SIZE) matching the Mojang mappings blit signature.
| import net.minecraft.client.gui.GuiGraphics; | ||
| import net.minecraft.client.renderer.RenderPipelines; | ||
| import net.minecraft.network.chat.Component; | ||
| import net.minecraft.resources.Identifier; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Identifier does not exist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in ba317eb — replaced all Identifier usages with ResourceLocation from net.minecraft.resources.ResourceLocation to match Official Mojang Mappings.
- Replace net.minecraft.resources.Identifier with ResourceLocation - Replace graphics.blitSprite() with graphics.blit() matching Mojang mappings API Co-authored-by: bubner <81782264+bubner@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
|
||
| float currentHpScale = Math.min(HEIGHT_SCALE, HEIGHT_SCALE * (hp / maxHp)); | ||
| hpScale = config.getInterpolateBars() ? Util.lerp(hpScale, currentHpScale, 0.1f) : currentHpScale; | ||
| int hpBarHeight = Math.round(hpScale); | ||
|
|
||
| float lowHpPercent = config.getAlertLowHpPercent(); | ||
| if (lowHpPercent >= 0 && healthPercent <= lowHpPercent && cycle) { | ||
| Util.drawBoldText(graphics, mc, "!!!", xCenter - 14, yCenter - 22, hpColour); | ||
| hpColour = config.getInterpolateColour() ? COLOR_RED : COLOR_WHITE; | ||
| graphics.fill(xCenter - 11, yCenter + 10 - HEIGHT_SCALE, xCenter - 11 + BAR_WIDTH, yCenter + 10 - hpBarHeight, Util.darkenRgb(hpColour, 0.25f)); | ||
| } | ||
|
|
||
| graphics.fill(xCenter - 11, yCenter + 10 - hpBarHeight, xCenter - 11 + BAR_WIDTH, yCenter + 10, hpColour); | ||
|
|
||
| // === Mana bar === | ||
| float manaPercentage = Math.min(100f, (mana / maxMana) * 100f); | ||
|
|
||
| int manaColour; | ||
| if (config.getInterpolateColour()) { | ||
| int r = Math.round(Util.lerp(200, 0, manaPercentage / 100f)); | ||
| int g = Math.round(Util.lerp(100, 255, manaPercentage / 100f)); | ||
| manaColour = Util.argb(255, r, g, 255); | ||
| } else { | ||
| manaColour = COLOR_AQUA; | ||
| } | ||
|
|
||
| float currentManaScale = Math.min(HEIGHT_SCALE, HEIGHT_SCALE * (mana / maxMana)); | ||
| manaScale = config.getInterpolateBars() ? Util.lerp(manaScale, currentManaScale, 0.1f) : currentManaScale; | ||
| int manaBarHeight = Math.round(manaScale); | ||
|
|
||
| float lowManaPercent = config.getAlertLowManaPercent(); | ||
| secInterval = manaReadStatus == ManaReadStatus.NOT_ENOUGH_MANA ? 0.2f : 0.4f; | ||
|
|
||
| if (manaReadStatus == ManaReadStatus.FROZEN) { | ||
| graphics.fill(xCenter + 9, yCenter + 10 - HEIGHT_SCALE, xCenter + 9 + BAR_WIDTH, yCenter + 10 - manaBarHeight, Util.darkenRgb(COLOR_GRAY, 0.25f)); | ||
| } else if (((lowManaPercent >= 0 && manaPercentage <= lowManaPercent) || manaReadStatus == ManaReadStatus.NOT_ENOUGH_MANA) && !cycle) { | ||
| int foreColour = manaReadStatus == ManaReadStatus.NOT_ENOUGH_MANA ? COLOR_RED : (config.getInterpolateColour() ? COLOR_AQUA : COLOR_WHITE); | ||
| Util.drawBoldText(graphics, mc, "!!!", xCenter + 7, yCenter + 13, foreColour); | ||
| manaColour = foreColour; | ||
| graphics.fill(xCenter + 9, yCenter + 10 - HEIGHT_SCALE, xCenter + 9 + BAR_WIDTH, yCenter + 10 - manaBarHeight, Util.darkenRgb(manaColour, 0.25f)); | ||
| } | ||
|
|
||
| int manaBarColour = manaReadStatus != ManaReadStatus.FROZEN ? manaColour : COLOR_GRAY; | ||
| graphics.fill(xCenter + 9, yCenter + 10 - manaBarHeight, xCenter + 9 + BAR_WIDTH, yCenter + 10, manaBarColour); | ||
|
|
||
| // === Percentages === | ||
| if (!config.getPercentage()) return; | ||
|
|
||
| float scale = 0.75f; | ||
| int lineHeight = Math.round(mc.font.lineHeight * scale / 2f); | ||
|
|
||
| // HP percentage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
too many magic numbers from the original javascript being used, e.g. xCenter + 9, yCenter + 10, xCenter - 32, xCenter - 28, xCenter + 15. move these and consolidate their uses as private static final integers as done with height scale, etc. also float scale = 0.75f can be moved out too. finally, making a functionality there if manaReadStatus reads NOT_ENOUGH_MANA -> it is desired for the not enough mana status to "latch" for two seconds, allowing the extended warning to take effect
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 121820b:
- Extracted all magic numbers into named
private static finalconstants:HP_BAR_X_OFFSET,MANA_BAR_X_OFFSET,BAR_BOTTOM_Y_OFFSET,HP_ALERT_X/Y_OFFSET,MANA_ALERT_X/Y_OFFSET,HP_PERCENT_X_OFFSET(_WIDE),MANA_PERCENT_X_OFFSET - Moved
float scale = 0.75ftoPERCENT_SCALEconstant - Added NOT_ENOUGH_MANA latch:
notEnoughManaLatchTickscounter set toNOT_ENOUGH_MANA_LATCH_TICKS(40 ticks = 2 seconds) when the status is detected, counting down inonTick(). During the latch window, neitherOKnorFROZENoverwrites the status, allowing the extended warning to take full effect.
… latch - Move all positional magic numbers from render() into private static final constants: HP_BAR_X_OFFSET, MANA_BAR_X_OFFSET, BAR_BOTTOM_Y_OFFSET, HP_ALERT_X/Y_OFFSET, MANA_ALERT_X/Y_OFFSET, HP_PERCENT_X_OFFSET(_WIDE), MANA_PERCENT_X_OFFSET - Move float scale = 0.75f to PERCENT_SCALE constant - Add NOT_ENOUGH_MANA latch: when mana status reads NOT_ENOUGH_MANA, the warning state persists for 2 seconds (40 ticks) via notEnoughManaLatchTicks counter, preventing premature reset when new action bar messages arrive Co-authored-by: bubner <81782264+bubner@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Ports the “Stats Ring” HUD overlay from a 1.8.9 ChatTriggers module into a 1.21.10 Fabric client mod, including persistent configuration and an in-game settings screen.
Changes:
- Added a HUD renderer that parses SkyBlock action bar stats and draws the ring overlay (HP/Mana bars, alerts, percentages).
- Introduced a Properties-backed config + Cloth Config GUI, wired to a
/ringclient command. - Added Cloth Config dependency + mod metadata updates and included the ring texture asset.
Reviewed changes
Copilot reviewed 9 out of 12 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/resources/fabric.mod.json | Declares Cloth Config as a dependency in mod metadata. |
| src/main/resources/assets/statsring/ring-2.png | Adds the ring backing texture asset. |
| src/main/java/me/bubner/statsring/Util.java | Adds shared helpers for parsing and rendering. |
| src/main/java/me/bubner/statsring/StatsRingRenderer.java | Implements action bar parsing + HUD rendering via HudElementRegistry. |
| src/main/java/me/bubner/statsring/StatsRing.java | Initializes config/renderer and registers /ring to open the config screen. |
| src/main/java/me/bubner/statsring/ModConfig.java | Adds Properties-based persistence for settings. |
| src/main/java/me/bubner/statsring/ManaReadStatus.java | Adds enum to track mana read state (OK/FROZEN/NOT_ENOUGH_MANA). |
| src/main/java/me/bubner/statsring/ConfigScreen.java | Adds Cloth Config-based settings UI and save wiring. |
| build.gradle | Adds Shedaniel Maven + Cloth Config dependency. |
| gradle.properties | Adds cloth_config_version. |
| gradlew | Adds the Gradle wrapper script. |
| README.md | Documents the Cloth Config dependency. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ModConfig.javawith Properties-based serialization (as specified)ConfigScreen.javausing Cloth Config for/ringsettings GUIStatsRingRenderer.javaporting main.js logic (action bar parsing + HUD rendering)StatsRing.javato register/ringcommand and wire componentsfabric.mod.jsonwith Cloth Config dependencyblitcall to usegraphics.blit(RenderPipelines.GUI_TEXTURED, ...)(Mojang mappings)IdentifiertoResourceLocation(Mojang mappings)Matrix3x2fStackwithpushMatrix/popMatrixHudElementRegistryinstead of deprecatedHudRenderCallbackUtil.javaManaReadStatusenum@authortagsscaletoPERCENT_SCALEconstant💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.