Skip to content

HUD Panels

satisfy edited this page Oct 21, 2026 · 2 revisions

HUD panels (net.satisfy.foundation.overlay)

Look at a block → a small panel tells the player what is inside and what to do next. Foundation draws the panel (position, scaling, background). You only describe the content.

1. Describe your block

public class ShelfInfoProvider implements BlockInfoProvider {
    @Override
    public List<InfoSection> describe(Level level, BlockPos pos, BlockState state, @Nullable BlockHitResult hit) {
        if (!(level.getBlockEntity(pos) instanceof ShelfBlockEntity shelf)) {
            return List.of();                       // not my block → next provider
        }
        return List.of(
                InfoSection.icons(Component.translatable("hud.mymod.on_shelf"), shelf.getItems(), InfoSection.ROW_COLUMNS),
                InfoSection.title(Component.translatable("hud.mymod.take_hint").withStyle(ChatFormatting.GRAY))
        );
    }
}

2. Register it (client init)

BlockInfoOverlay.registerProvider(new ShelfInfoProvider());

The first provider that returns a non-empty list wins.

Building blocks: InfoSection

Factory Shows
InfoSection.title(text) one line of text
InfoSection.icons(title, items, columns) title + item grid (ROW_COLUMNS = 6, GRID_COLUMNS = 3)
InfoSection.lines(title, lines) title + several text lines
InfoSection.rows(title, rows) title + icon/text rows (Row.item(...), Row.sprite(...))
InfoSection.image(title, texture, size) title + a picture (e.g. a "toss!" icon)

Modifiers: withLines(...), withRows(...), withPlaceholder(texture), withDecorations().

Extras

  • Panels for blocks you don't look at – e.g. "your wok needs a toss":
    BlockInfoOverlay.registerTracker(() -> myTrackedPositions);
    Your provider then gets hit == null for those positions.
  • Coloured border – override beforeBackground(...) and call TooltipBorder.markHot() / markSoul() / mark(top, bottom).
  • Short notice – BlockInfoOverlay.showNotice(pos, message).
  • Server data the client doesn't have – register a BlockInfoSync.Handler on the server, read it on the client:
    BlockInfoSync.registerHandler((level, pos, player, tag) -> tag.putInt("stock", myStock(level, pos)));
    CompoundTag data = BlockInfoSync.poll(level, pos); // in describe(...)

Config switch

Every panel is switched by your mod's config. Return List.of() from describe when your mod's "block info" option is off. Foundation's own config only has showBlockNotices, which hides the notices from BlockNotice.send(...) (see Config).

Notices from the server

BlockInfoOverlay.showNotice(pos, text) shows a short golden notice at a block, client side. From the server use BlockNotice.send(serverPlayer, pos, text), or send(serverPlayer, pos, title, lines) for a title with more lines below.

Panels for entities

Same idea for mobs and other entities: look at it, get a panel above it.

public class CowInfoProvider implements EntityInfoProvider {
    @Override
    public List<InfoSection> describe(Level level, Entity entity, EntityHitResult hit) {
        if (!MyConfig.showCowInfo() || !(entity instanceof Cow cow)) {
            return List.of();
        }
        CompoundTag data = EntityInfoSync.poll(cow);
        return List.of(
                InfoSection.title(Component.translatable("hud.mymod.cow.milk", data.getInt("milk")).withStyle(ChatFormatting.GRAY)),
                InfoSection.title(Component.translatable("hud.mymod.cow.hint").withStyle(ChatFormatting.GRAY))
        );
    }
}
BlockInfoOverlay.registerEntityProvider(new CowInfoProvider());
  • The first provider that returns a non-empty list wins, like for blocks.
  • Override beforeBackground(level, entity) for a coloured border (TooltipBorder).
  • Server data: EntityInfoSync.registerHandler((level, entity, player, tag) -> ...) on the server, EntityInfoSync.poll(entity) in describe. The client asks every 10 ticks, only for entities within 8 blocks. Until the answer arrives poll returns an empty tag.

Screen notices

A notice in the top middle of the screen, not tied to a block:

BlockInfoOverlay.showScreenNotice(Component.translatable("hud.mymod.timer.done"), List.of());
BlockInfoOverlay.clearScreenNotice();

It stays for about 2 seconds and fades out. Call showScreenNotice again every tick to keep it on screen (e.g. while a timer runs), clearScreenNotice() removes it right away. Client side only.

Clone this wiki locally