Client code
Put client-only code in a ClientModInitializer. The dedicated server never reads this class, so client code never runs on a server.
Create a new file, CrystalGardenClient.java.
public final class CrystalGardenClient implements ClientModInitializer {
@Override
public void onInitializeClient(ModContext mod) {
CrystalGardenMod.handshake.clientAnswer(request -> CrystalGardenMod.MOD_ID + " " + mod.modVersion());
ClientRegistrar client = mod.client();
// A growth bar and the stage over each sprout, from the block entity data the server syncs.
client.blockEntityRenderer(CrystalGardenMod.CRYSTAL_SPROUT, (sprout, canvas) -> {
int stage = sprout.world().stateAt(sprout.pos()).get(CrystalSprout.STAGE);
if (stage >= CrystalSprout.RIPE) {
canvas.item(CrystalGardenMod.CRYSTAL, 0.5, 1.3, 0.5, 0.4f, canvas.animationTime() * 4 % 360);
canvas.text("Ripe!", 0.5, 1.7, 0.5, 0.02f, 0xFFE1BEE7);
return;
}
int percent = sprout.data().getInt("growth", 0) * 100 / CrystalSprout.TICKS_PER_STAGE;
canvas.text("Stage " + stage + " · " + percent + "%", 0.5, 1.2, 0.5, 0.015f, 0xFFFFFFFF);
});
// The HUD shows the player's own harvest count (entity data synced to its owner).
KeyBinding toggle = client.keyBinding("toggle_hud", KeyCodes.G);
AtomicBoolean hudVisible = new AtomicBoolean(true);
AtomicInteger harvested = new AtomicInteger();
mod.events().register(ClientEvents.TICK, view -> {
while (toggle.consumeClick()) {
hudVisible.set(!hudVisible.get());
}
view.player()
.ifPresent(player -> harvested.set(
player.data(CrystalGardenMod.harvested).data().getInt("count", 0)));
});
client.hudLayer("harvest_counter", canvas -> {
if (!hudVisible.get()) {
return;
}
String text = "Crystals: " + harvested.get();
canvas.fill(2, 2, 8 + canvas.textWidth(text), 14, 0x80000000);
canvas.drawText(text, 5, 4, 0xFFE1BEE7);
});
}
}
| Part | What it does |
|---|---|
blockEntityRenderer | Called every frame for each sprout. While it grows, it draws the stage and percent. When it is fully grown, it draws a spinning crystal and Ripe!. The data is the block entity data the server sent with syncToClients() |
keyBinding("toggle_hud", KeyCodes.G) | A key that appears in the controls settings. The player can change it to another key |
ClientEvents.TICK | On every client tick, handles key input and reads the harvest count from the synced player data |
hudLayer | Draws text like Crystals: 3 at the top left of the screen. The drawing code uses only the values read in the tick |
The key names come from the translation files.
{
"key.crystal_garden.toggle_hud": "Toggle Crystal Counter",
"key.category.crystal_garden.keybinds": "Crystal Garden"
}
Also add "key.crystal_garden.toggle_hud": "크리스털 수 표시 켜기/끄기" and "key.category.crystal_garden.keybinds": "크리스털 정원" to ko_kr.json. The entries you write by hand in en_us.json are merged with the names polyResources generates.
This class runs only if entrypoints.client in poly.mod.json lists it (see poly.mod.json).
- If an exception occurs in a HUD layer or in drawing code, only that layer is turned off and
POLY-CLIENT-003is logged. - Calling
mod.client()on a dedicated server givesPOLY-CLIENT-001.
Check
If you place a sprout with runClient, a growth display like Stage 0 · 45% appears above it. When you harvest, the number at the top left goes up. Press G to hide it or show it again.
Screens (GUI), drawing directly into the world, and shaders are in Client basics, Screens, and Rendering.
Next step
Check the features automatically with game tests.