A HUD overlay
Status: Implemented. Mod layers are drawn after the vanilla HUD (ordering among vanilla layers, and moving or replacing vanilla layers, are Planned).
Goal: a small panel in the top-left corner with the player’s coordinates and the number of rubies they carry, hidden while F1 is on (automatic) and when the player has none.
Language: Lua, with C++ at the end. APIs: HUD layers, Screens, widgets and 2d drawing.
1. Cache on the tick, draw on the frame
A HUD layer runs every frame (hundreds of times a second); the values change at most once per tick (20 times a second). So compute on the client tick and only draw in the layer:
local lines = nil
cl.on("tick.client_post", function()
local p = cl.entity.client_player()
if not p then lines = nil return end
local n = p:count_item("my_mod:ruby")
if n == 0 then lines = nil return end
local pos = p.pos
lines = {
("XYZ: %d %d %d"):format(math.floor(pos.x), math.floor(pos.y), math.floor(pos.z)),
("Rubies: %d"):format(n),
}
end)
cl.hud.register_layer{
id = "ruby_panel",
visible = function() return lines ~= nil end,
render = function(g)
local h = #lines * 10 + 4
g:fill(2, 2, 110, 2 + h, 0x80000000) -- half-transparent black
for i, line in ipairs(lines) do
g:text(line, 5, 5 + (i - 1) * 10, 0xFFFFFF, true)
end
end,
}
2. What makes this cheap
visiblereturns false when there’s nothing to show, sorenderisn’t even called.- The tick handler does the counting; the frame only draws two lines of text.
- Without any registered layer, the HUD hook costs one load per frame.
3. C++
class PanelMod final : public clight::Mod {
std::string coords_, rubies_;
bool show_ = false;
public:
void on_client_setup() override {
clight::on<clight::ev::TickClientPost>([this](cl_ev_tick&) {
clight::Entity p = clight::entity::client_player();
const int32_t n = p ? p.count_item("my_mod:ruby") : 0;
show_ = n > 0;
if (!show_) return;
const cl_vec3 v = p.pos();
coords_ = std::format("XYZ: {} {} {}", int(std::floor(v.x)), int(std::floor(v.y)), int(std::floor(v.z)));
rubies_ = std::format("Rubies: {}", n);
}).detach();
clight::hud::add_layer("my_mod:ruby_panel", [this](clight::Gui g, float) {
g.fill(2, 2, 110, 26, 0x80000000);
g.text(coords_, 5, 5, 0xFFFFFF, true);
g.text(rubies_, 5, 15, 0xFFFFFF, true);
}, {.visible = [this] { return show_; }});
}
};
CLIGHT_MOD(PanelMod)
Planned
Placing a layer between vanilla layers (above = "minecraft:food"), hiding a vanilla layer to replace it, and wrapping a vanilla layer to move or tint it.