Skip to content

HUD System

HudRenderer

The lowest-level abstraction — anything that can draw onto the HUD:

@FunctionalInterface
public interface HudRenderer {
    void render(GuiGraphicsExtractor extractor, DeltaTracker deltaTracker);
}

Register one via Corim.bridge().registerHudElement(Identifier id, HudRenderer renderer). If you just need a single custom HUD element, you can implement HudRenderer directly and stop there. For a full text-line system with positioning, dragging, and auto-stacking (what CorimLib itself uses for its ~21 HUD lines), keep reading.

HudLineFeature

An abstract base class combining a Feature (so the line is toggleable and shows up in the settings GUI) with x/y/scale/customPosition settings and a text(Minecraft) method you override:

public abstract class HudLineFeature {
    public final Feature feature;
    public final IntSetting x;
    public final IntSetting y;
    public final DecimalSetting scale;
    public final BooleanSetting customPosition;

    protected HudLineFeature(String id, String name, String description,
                              boolean defaultEnabled, int defaultX, int defaultY);

    /** Returns the text to draw this frame, or null/blank to draw nothing. */
    public abstract String text(Minecraft mc);
}

Constructing a HudLineFeature subclass automatically calls FeatureRegistry.register(feature) and adds itself to HudRegistry.LINES — you don't register it separately. The feature.id() is "hud." + id (the constructor prepends the prefix for you).

Real example — CorimLib's own FPS line, in full:

new HudLineFeature("fps", "FPS", "Shows current frames per second.", true, 4, 4) {
    @Override
    public String text(Minecraft mc) {
        return "FPS: " + FrameStats.fps();
    }
};

And one that returns null conditionally (so it draws nothing rather than an empty line):

new HudLineFeature("ping", "Ping", "Shows your connection latency in milliseconds.", false, 4, 14) {
    @Override
    public String text(Minecraft mc) {
        if (mc.player == null || mc.getConnection() == null) return null;
        var info = mc.getConnection().getPlayerInfo(mc.player.getUUID());
        return info == null ? null : "Ping: " + info.getLatency() + "ms";
    }
};

Auto-stacking, no gaps

By default, HUD lines don't render at fixed positions forever — they auto-stack with the other enabled, non-custom-positioned lines, ordered by whichever order they were most recently enabled in, re-derived every frame. A line that's currently disabled — or currently returns null/blank text, like Ping above when disconnected — doesn't leave a gap where it would have been. Dragging a line in the HUD Editor sets its customPosition setting to true, which pins it at its dragged x/y instead of participating in the stack; toggling customPosition back off (or using "Reset All Positions" in the editor) rejoins the auto stack.

HudRegistry

The registry backing all of this — also where the stacking math actually lives:

public final class HudRegistry {
    public static final List<HudLineFeature> LINES;
    public static final int STACK_BASE_X; // 4
    public static final int STACK_BASE_Y; // 4
    public static final int STACK_LINE_HEIGHT; // 10

    public static int nextOrder();
    public static List<HudLineFeature> autoStackOrder();
    public static Map<HudLineFeature, int[]> effectivePositions();
}

effectivePositions() is shared by the live HUD renderer and the HUD Editor's drag preview/hit-testing, specifically so what you see while dragging a line is guaranteed to match what actually renders — there's no separate "preview math" that could drift out of sync with the real thing.

HudOverlayElement

The actual HudRenderer implementation that draws every enabled HudLineFeature — this is what gets registered once via Corim.bridge().registerHudElement(...), rather than registering one HUD element per line:

public final class HudOverlayElement implements HudRenderer {
    public static boolean editorOpen; // set true while CrunchHudEditorScreen is open to suppress the live overlay
    @Override
    public void render(GuiGraphicsExtractor extractor, DeltaTracker deltaTracker) { /* ... */ }
}

Registering it:

Corim.bridge().registerHudElement(
        Identifier.fromNamespaceAndPath("mymod", "hud_overlay"),
        new HudOverlayElement());

If you're building your own mod, register your own HudOverlayElement() under your own namespace, or implement HudRenderer directly if you don't need the full auto-stacking line system.

FrameStats

A small self-counted (not read from a vanilla field) FPS/frame-time tracker used by the FPS HUD line and CrunchPerformanceMonitorScreen's rolling graph:

Corim.bridge().onClientTick or per-render: FrameStats.onFrame();
int fps = FrameStats.fps();

HudOverlayElement.render(...) already calls FrameStats.onFrame() on every frame, so if you're using the standard HudOverlayElement/HudLineFeature flow, FrameStats is already being fed for you.