Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Feb 8, 2026

  • Analyze ChatTriggers source code (main.js + config.js) from main branch
  • Understand scaffolded Fabric mod template on 1.21.10-fabric branch
  • Add Cloth Config dependency to build.gradle for config GUI (version 20.0.148)
  • Create ModConfig.java with Properties-based serialization (as specified)
  • Create ConfigScreen.java using Cloth Config for /ring settings GUI
  • Create StatsRingRenderer.java porting main.js logic (action bar parsing + HUD rendering)
  • Update StatsRing.java to register /ring command and wire components
  • Update fabric.mod.json with Cloth Config dependency
  • Address all PR review feedback:
    • Fix blit call to use graphics.blit(RenderPipelines.GUI_TEXTURED, ...) (Mojang mappings)
    • Fix Identifier to ResourceLocation (Mojang mappings)
    • Fix pose transforms to use Matrix3x2fStack with pushMatrix/popMatrix
    • Use HudElementRegistry instead of deprecated HudRenderCallback
    • Replace unicode escape sequences with actual characters
    • Move utility methods to Util.java
    • Add ManaReadStatus enum
    • Use local var for config, remove init log
    • Fix all @author tags
    • Extract magic numbers into named constants
    • Move scale to PERCENT_SCALE constant
    • Add NOT_ENOUGH_MANA 2-second latch for extended warning

💡 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.

Copilot AI and others added 3 commits February 8, 2026 10:05
- 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>
Copilot AI changed the title [WIP] Port Stats Ring from ChatTriggers to Fabric mod Port Stats Ring from ChatTriggers module to 1.21.10 Fabric mod Feb 8, 2026
Copilot AI requested a review from bubner February 8, 2026 10:16
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);
Copy link
Owner

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

Copy link
Contributor Author

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();
Copy link
Owner

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

Copy link
Contributor Author

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);
Copy link
Owner

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bf31df0StatsRingRenderer now implements HudElement and registers via HudElementRegistry.attachElementAfter(VanillaHudElements.CROSSHAIR, ...).

String msg = message.getString();

// Extract health information
if (msg.contains("\u2764")) {
Copy link
Owner

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

Copy link
Contributor Author

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 .

Comment on lines 227 to 264
// --- 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);
}
Copy link
Owner

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

Copy link
Contributor Author

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;
Copy link
Owner

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

Copy link
Contributor Author

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");
Copy link
Owner

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

Copy link
Contributor Author

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
Copy link
Owner

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

Copy link
Contributor Author

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>
Copilot AI requested a review from bubner February 8, 2026 10:40
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);
Copy link
Owner

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

Copy link
Contributor Author

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;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Identifier does not exist

Copy link
Contributor Author

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>
Copy link
Contributor Author

Copilot AI commented Feb 8, 2026

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:

  • maven.fabricmc.net
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED -Xmx1G -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from bubner February 8, 2026 10:51
@bubner bubner linked an issue Feb 9, 2026 that may be closed by this pull request
Comment on lines 141 to 192

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
Copy link
Owner

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

Copy link
Contributor Author

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 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
  • Moved float scale = 0.75f to PERCENT_SCALE constant
  • Added NOT_ENOUGH_MANA latch: notEnoughManaLatchTicks counter set to NOT_ENOUGH_MANA_LATCH_TICKS (40 ticks = 2 seconds) when the status is detected, counting down in onTick(). During the latch window, neither OK nor FROZEN overwrites 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>
Copilot AI requested a review from bubner February 9, 2026 08:10
@bubner bubner marked this pull request as ready for review February 9, 2026 09:02
Copilot AI review requested due to automatic review settings February 9, 2026 09:02
Copy link

Copilot AI left a 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 /ring client 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.

@bubner bubner merged commit f50d72e into 1.21.10-fabric Feb 9, 2026
2 checks passed
@bubner bubner deleted the copilot/port-stats-ring-to-fabric branch February 9, 2026 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update/port to latest version

2 participants