Example Repository
For a complete working example, see the betterhud-fabric-example mod.Build Configuration
Gradle (Kotlin DSL)
build.gradle.kts
plugins {
id("fabric-loom") version "1.9.+"
kotlin("jvm") version "2.1.0"
}
repositories {
mavenCentral()
maven("https://maven.fabricmc.net/")
}
val minecraftVersion = "1.21.4"
val fabricLoaderVersion = "0.16.10"
val fabricApiVersion = "0.114.0+1.21.4"
dependencies {
minecraft("com.mojang:minecraft:${minecraftVersion}")
mappings("net.fabricmc:yarn:${minecraftVersion}+build.1:v2")
modImplementation("net.fabricmc:fabric-loader:${fabricLoaderVersion}")
modImplementation("net.fabricmc.fabric-api:fabric-api:${fabricApiVersion}")
// BetterHud dependencies
modCompileOnly("io.github.toxicity188:BetterHud-fabric-api:VERSION")
compileOnly("io.github.toxicity188:BetterHud-standard-api:VERSION")
compileOnly("io.github.toxicity188:BetterCommand:VERSION")
}
Gradle (Groovy)
build.gradle
plugins {
id 'fabric-loom' version '1.9.+'
id 'java'
}
repositories {
mavenCentral()
maven { url 'https://maven.fabricmc.net/' }
}
dependencies {
minecraft 'com.mojang:minecraft:1.21.4'
mappings 'net.fabricmc:yarn:1.21.4+build.1:v2'
modImplementation 'net.fabricmc:fabric-loader:0.16.10'
modImplementation 'net.fabricmc.fabric-api:fabric-api:0.114.0+1.21.4'
modCompileOnly 'io.github.toxicity188:BetterHud-fabric-api:VERSION'
compileOnly 'io.github.toxicity188:BetterHud-standard-api:VERSION'
compileOnly 'io.github.toxicity188:BetterCommand:VERSION'
}
Mod Setup
fabric.mod.json
fabric.mod.json
{
"schemaVersion": 1,
"id": "betterhud-example",
"version": "1.0.0",
"name": "BetterHud Example",
"description": "Example integration with BetterHud",
"authors": ["YourName"],
"contact": {},
"license": "MIT",
"icon": "assets/betterhud-example/icon.png",
"environment": "server",
"entrypoints": {
"main": [
"com.example.betterhudexample.BetterHudExample"
]
},
"depends": {
"fabricloader": ">=0.16.0",
"fabric-api": "*",
"minecraft": ">=1.21",
"betterhud": "*"
}
}
Main Mod Class
BetterHudExample.java
package com.example.betterhudexample;
import kr.toxicity.hud.api.BetterHudAPI;
import kr.toxicity.hud.api.fabric.FabricBootstrap;
import kr.toxicity.hud.api.fabric.event.EventRegistry;
import kr.toxicity.hud.api.manager.PlaceholderManager;
import kr.toxicity.hud.api.placeholder.HudPlaceholder;
import kr.toxicity.hud.api.plugin.ReloadState;
import net.fabricmc.api.ModInitializer;
import net.minecraft.server.network.ServerPlayerEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BetterHudExample implements ModInitializer {
public static final String MOD_ID = "betterhud-example";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
private static BetterHudExample instance;
@Override
public void onInitialize() {
instance = this;
LOGGER.info("Initializing BetterHud Example Mod");
try {
// Wait for BetterHud to load, then register our components
FabricBootstrap.POST_RELOAD_EVENT.register(this::onBetterHudReload);
// Register custom events
CustomEvents.register();
LOGGER.info("BetterHud Example initialized successfully!");
} catch (Exception e) {
LOGGER.error("Failed to initialize BetterHud integration", e);
}
}
private void onBetterHudReload(ReloadState state) {
if (state.isSuccess()) {
LOGGER.info("BetterHud reloaded, registering placeholders...");
registerPlaceholders();
registerTriggers();
} else {
LOGGER.warn("BetterHud reload failed: " + state.getMessage());
}
}
private void registerPlaceholders() {
PlaceholderManager manager = BetterHudAPI.inst().getPlaceholderManager();
// Register number placeholder - player experience level
HudPlaceholder.<Number>builder()
.function((args, event) -> player -> {
ServerPlayerEntity serverPlayer =
(ServerPlayerEntity) player.handle();
return serverPlayer.experienceLevel;
})
.build()
.add("player_exp_level", manager.getNumberContainer());
// Register string placeholder - current dimension
HudPlaceholder.<String>builder()
.function((args, event) -> player -> {
ServerPlayerEntity serverPlayer =
(ServerPlayerEntity) player.handle();
return serverPlayer.getWorld().getRegistryKey().getValue().toString();
})
.build()
.add("player_dimension", manager.getStringContainer());
// Register boolean placeholder - is in nether
HudPlaceholder.<Boolean>builder()
.function((args, event) -> player -> {
ServerPlayerEntity serverPlayer =
(ServerPlayerEntity) player.handle();
return serverPlayer.getWorld().getRegistryKey().getValue()
.getPath().equals("the_nether");
})
.build()
.add("in_nether", manager.getBooleanContainer());
LOGGER.info("Registered custom placeholders");
}
private void registerTriggers() {
// Register custom event triggers
CustomTriggers.register();
LOGGER.info("Registered custom triggers");
}
public static BetterHudExample getInstance() {
return instance;
}
}
Custom Fabric Events
Defining Custom Events
CustomEvents.java
package com.example.betterhudexample;
import kr.toxicity.hud.api.fabric.event.EventRegistry;
import kr.toxicity.hud.api.fabric.event.PlayerEvent;
import net.minecraft.server.network.ServerPlayerEntity;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
public class CustomEvents {
// Custom player skill event
public static final EventRegistry<SkillUseEvent> SKILL_USE = new EventRegistry<>();
// Custom player achievement event
public static final EventRegistry<CustomAchievementEvent> CUSTOM_ACHIEVEMENT = new EventRegistry<>();
public static void register() {
// Events are registered by creating the EventRegistry instances above
BetterHudExample.LOGGER.info("Custom events registered");
}
// Custom event class for skill usage
public static class SkillUseEvent implements PlayerEvent {
private final ServerPlayerEntity player;
private final String skillName;
private final int skillLevel;
public SkillUseEvent(ServerPlayerEntity player, String skillName, int skillLevel) {
this.player = player;
this.skillName = skillName;
this.skillLevel = skillLevel;
}
@Override
public @NotNull UUID getPlayerUUID() {
return player.getUuid();
}
public ServerPlayerEntity getPlayer() {
return player;
}
public String getSkillName() {
return skillName;
}
public int getSkillLevel() {
return skillLevel;
}
}
// Custom achievement event
public static class CustomAchievementEvent implements PlayerEvent {
private final ServerPlayerEntity player;
private final String achievementId;
private final int points;
public CustomAchievementEvent(ServerPlayerEntity player, String achievementId, int points) {
this.player = player;
this.achievementId = achievementId;
this.points = points;
}
@Override
public @NotNull UUID getPlayerUUID() {
return player.getUuid();
}
public ServerPlayerEntity getPlayer() {
return player;
}
public String getAchievementId() {
return achievementId;
}
public int getPoints() {
return points;
}
}
}
Custom Triggers
CustomTriggers.java
package com.example.betterhudexample;
import kr.toxicity.hud.api.BetterHudAPI;
import kr.toxicity.hud.api.fabric.trigger.HudFabricEventTrigger;
import kr.toxicity.hud.api.manager.TriggerManager;
import kr.toxicity.hud.api.update.UpdateEvent;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
public class CustomTriggers {
public static void register() {
TriggerManager manager = BetterHudAPI.inst().getTriggerManager();
// Register skill use trigger
manager.addTrigger("skill_use", new HudFabricEventTrigger<>() {
@Override
public @NotNull Object getKey(CustomEvents.SkillUseEvent event) {
return event.getPlayerUUID();
}
@Override
public @NotNull EventRegistry<CustomEvents.SkillUseEvent> registry() {
return CustomEvents.SKILL_USE;
}
});
// Register achievement trigger with custom variables
manager.addTrigger("custom_achievement", new HudFabricEventTrigger<>() {
@Override
public @NotNull Object getKey(CustomEvents.CustomAchievementEvent event) {
return event.getPlayerUUID();
}
@Override
public @NotNull EventRegistry<CustomEvents.CustomAchievementEvent> registry() {
return CustomEvents.CUSTOM_ACHIEVEMENT;
}
});
}
}
Triggering Custom Events
SkillSystem.java
package com.example.betterhudexample;
import kr.toxicity.hud.api.BetterHudAPI;
import kr.toxicity.hud.api.manager.PlayerManager;
import kr.toxicity.hud.api.player.HudPlayer;
import kr.toxicity.hud.api.update.UpdateEvent;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.HashMap;
import java.util.Map;
public class SkillSystem {
public static void useSkill(ServerPlayerEntity player, String skillName, int level) {
// Create and invoke the custom event
CustomEvents.SkillUseEvent event = new CustomEvents.SkillUseEvent(
player,
skillName,
level
);
// Trigger the event through the EventRegistry
CustomEvents.SKILL_USE.invoke(event);
// Also show a popup with skill info
showSkillPopup(player, skillName, level);
}
private static void showSkillPopup(ServerPlayerEntity player, String skillName, int level) {
PlayerManager playerManager = BetterHudAPI.inst().getPlayerManager();
HudPlayer hudPlayer = playerManager.getHudPlayer(player.getUuid());
if (hudPlayer == null) return;
var popupManager = BetterHudAPI.inst().getPopupManager();
var popup = popupManager.getPopup("skill_use_popup");
if (popup != null) {
// Create variables for the popup
Map<String, String> variables = new HashMap<>();
variables.put("skill_name", skillName);
variables.put("skill_level", String.valueOf(level));
// Create update event with variables
UpdateEvent updateEvent = UpdateEvent.builder()
.variables(variables)
.build();
try {
popup.show(hudPlayer, updateEvent);
} catch (Exception e) {
BetterHudExample.LOGGER.error("Failed to show skill popup", e);
}
}
}
public static void grantAchievement(ServerPlayerEntity player, String achievementId, int points) {
// Create achievement event
CustomEvents.CustomAchievementEvent event = new CustomEvents.CustomAchievementEvent(
player,
achievementId,
points
);
// Invoke the event
CustomEvents.CUSTOM_ACHIEVEMENT.invoke(event);
BetterHudExample.LOGGER.info("Player {} earned achievement: {}",
player.getName().getString(), achievementId);
}
}
Working with Players
FabricPlayerHelper.java
package com.example.betterhudexample;
import kr.toxicity.hud.api.BetterHudAPI;
import kr.toxicity.hud.api.manager.PlayerManager;
import kr.toxicity.hud.api.player.HudPlayer;
import net.minecraft.server.network.ServerPlayerEntity;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class FabricPlayerHelper {
@Nullable
public static HudPlayer getHudPlayer(ServerPlayerEntity player) {
PlayerManager manager = BetterHudAPI.inst().getPlayerManager();
return manager.getHudPlayer(player.getUuid());
}
@Nullable
public static HudPlayer getHudPlayer(UUID uuid) {
PlayerManager manager = BetterHudAPI.inst().getPlayerManager();
return manager.getHudPlayer(uuid);
}
public static void disableHud(ServerPlayerEntity player) {
HudPlayer hudPlayer = getHudPlayer(player);
if (hudPlayer != null) {
hudPlayer.setHudEnabled(false);
}
}
public static void enableHud(ServerPlayerEntity player) {
HudPlayer hudPlayer = getHudPlayer(player);
if (hudPlayer != null) {
hudPlayer.setHudEnabled(true);
}
}
public static void updatePlayerVariable(ServerPlayerEntity player, String key, String value) {
HudPlayer hudPlayer = getHudPlayer(player);
if (hudPlayer != null) {
hudPlayer.getVariableMap().put(key, value);
}
}
}
Advanced Placeholder Examples
AdvancedPlaceholders.java
package com.example.betterhudexample;
import kr.toxicity.hud.api.BetterHudAPI;
import kr.toxicity.hud.api.placeholder.HudPlaceholder;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.stat.Stats;
import net.minecraft.util.math.BlockPos;
public class AdvancedPlaceholders {
public static void register() {
var manager = BetterHudAPI.inst().getPlaceholderManager();
// Placeholder with arguments - player stat
HudPlaceholder.<Number>builder()
.requiredArgsLength(1)
.function((args, event) -> {
String statType = args.get(0);
return player -> {
ServerPlayerEntity serverPlayer = (ServerPlayerEntity) player.handle();
return switch (statType.toLowerCase()) {
case "deaths" -> serverPlayer.getStatHandler()
.getStat(Stats.CUSTOM.getOrCreateStat(Stats.DEATHS));
case "jumps" -> serverPlayer.getStatHandler()
.getStat(Stats.CUSTOM.getOrCreateStat(Stats.JUMP));
case "playtime" -> serverPlayer.getStatHandler()
.getStat(Stats.CUSTOM.getOrCreateStat(Stats.PLAY_TIME)) / 20;
default -> 0;
};
};
})
.build()
.add("player_stat", manager.getNumberContainer());
// Player coordinates
HudPlaceholder.<Number>builder()
.requiredArgsLength(1)
.function((args, event) -> {
String coordinate = args.get(0);
return player -> {
ServerPlayerEntity serverPlayer = (ServerPlayerEntity) player.handle();
BlockPos pos = serverPlayer.getBlockPos();
return switch (coordinate.toLowerCase()) {
case "x" -> pos.getX();
case "y" -> pos.getY();
case "z" -> pos.getZ();
default -> 0;
};
};
})
.build()
.add("player_coord", manager.getNumberContainer());
// Time of day
HudPlaceholder.<String>builder()
.function((args, event) -> player -> {
ServerPlayerEntity serverPlayer = (ServerPlayerEntity) player.handle();
long timeOfDay = serverPlayer.getWorld().getTimeOfDay() % 24000;
if (timeOfDay < 6000) return "morning";
else if (timeOfDay < 12000) return "day";
else if (timeOfDay < 18000) return "evening";
else return "night";
})
.build()
.add("time_of_day", manager.getStringContainer());
}
}
Compass Integration
FabricCompassHelper.java
package com.example.betterhudexample;
import kr.toxicity.hud.api.BetterHudAPI;
import kr.toxicity.hud.api.adapter.LocationWrapper;
import kr.toxicity.hud.api.player.HudPlayer;
import kr.toxicity.hud.api.player.PointedLocation;
import kr.toxicity.hud.api.player.PointedLocationProvider;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import java.util.HashSet;
import java.util.Set;
public class FabricCompassHelper {
public static void addCompassPoint(
ServerPlayerEntity player,
BlockPos pos,
String name,
String icon
) {
var playerManager = BetterHudAPI.inst().getPlayerManager();
HudPlayer hudPlayer = playerManager.getHudPlayer(player.getUuid());
if (hudPlayer != null) {
LocationWrapper location = LocationWrapper.of(
player.getWorld().getRegistryKey().getValue().toString(),
pos.getX(),
pos.getY(),
pos.getZ()
);
PointedLocation pointedLocation = PointedLocation.builder()
.location(location)
.name(name)
.icon(icon)
.build();
hudPlayer.pointers().add(pointedLocation);
}
}
public static void registerWorldSpawnProvider() {
var playerManager = BetterHudAPI.inst().getPlayerManager();
playerManager.addLocationProvider(new PointedLocationProvider() {
@Override
public Set<PointedLocation> provide(HudPlayer player) {
Set<PointedLocation> locations = new HashSet<>();
ServerPlayerEntity serverPlayer = (ServerPlayerEntity) player.handle();
BlockPos spawnPos = serverPlayer.getWorld().getSpawnPos();
locations.add(PointedLocation.builder()
.location(LocationWrapper.of(
serverPlayer.getWorld().getRegistryKey().getValue().toString(),
spawnPos.getX(),
spawnPos.getY(),
spawnPos.getZ()
))
.name("World Spawn")
.icon("spawn")
.build());
return locations;
}
});
}
}
Error Handling
SafeFabricOperations.java
package com.example.betterhudexample;
import kr.toxicity.hud.api.BetterHudAPI;
import kr.toxicity.hud.api.update.UpdateEvent;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.HashMap;
public class SafeFabricOperations {
public static void safeShowPopup(
ServerPlayerEntity player,
String popupName,
HashMap<String, String> variables
) {
try {
var playerManager = BetterHudAPI.inst().getPlayerManager();
var hudPlayer = playerManager.getHudPlayer(player.getUuid());
if (hudPlayer == null) {
BetterHudExample.LOGGER.warn(
"HudPlayer not found for {}",
player.getName().getString()
);
return;
}
var popupManager = BetterHudAPI.inst().getPopupManager();
var popup = popupManager.getPopup(popupName);
if (popup == null) {
BetterHudExample.LOGGER.warn("Popup '{}' not found!", popupName);
return;
}
UpdateEvent event = UpdateEvent.builder()
.variables(variables != null ? variables : new HashMap<>())
.build();
popup.show(hudPlayer, event);
} catch (Exception e) {
BetterHudExample.LOGGER.error(
"Failed to show popup to {}",
player.getName().getString(),
e
);
}
}
}
Next Steps
Custom HUD
Learn how to create custom HUD elements
Custom Popup
Create dynamic popup notifications
Fabric API Reference
Complete Fabric API documentation
Standard API
Core API reference
