Skip to main content
This guide shows how to create a complete Bukkit plugin that integrates with BetterHud, including custom placeholders, HUD triggers, and popup management.

Example Repository

For a complete working example, see the BetterHud-MMOCore integration plugin.

Build Configuration

Gradle (Kotlin DSL)

build.gradle.kts
plugins {
    kotlin("jvm") version "2.1.0"
    id("com.github.johnrengelman.shadow") version "8.1.1"
}

repositories {
    mavenCentral()
    maven("https://repo.papermc.io/repository/maven-public/")
}

dependencies {
    compileOnly("io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT")
    compileOnly("io.github.toxicity188:BetterHud-standard-api:VERSION")
    compileOnly("io.github.toxicity188:BetterHud-bukkit-api:VERSION")
    compileOnly("io.github.toxicity188:BetterCommand:VERSION")
}

tasks {
    shadowJar {
        archiveClassifier.set("")
    }
}

Gradle (Groovy)

build.gradle
plugins {
    id 'java'
    id 'com.github.johnrengelman.shadow' version '8.1.1'
}

repositories {
    mavenCentral()
    maven { url 'https://repo.papermc.io/repository/maven-public/' }
}

dependencies {
    compileOnly 'io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT'
    compileOnly 'io.github.toxicity188:BetterHud-standard-api:VERSION'
    compileOnly 'io.github.toxicity188:BetterHud-bukkit-api:VERSION'
    compileOnly 'io.github.toxicity188:BetterCommand:VERSION'
}

Maven

pom.xml
<repositories>
    <repository>
        <id>papermc</id>
        <url>https://repo.papermc.io/repository/maven-public/</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>io.papermc.paper</groupId>
        <artifactId>paper-api</artifactId>
        <version>1.21.4-R0.1-SNAPSHOT</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>io.github.toxicity188</groupId>
        <artifactId>BetterHud-standard-api</artifactId>
        <version>VERSION</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>io.github.toxicity188</groupId>
        <artifactId>BetterHud-bukkit-api</artifactId>
        <version>VERSION</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>io.github.toxicity188</groupId>
        <artifactId>BetterCommand</artifactId>
        <version>VERSION</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Plugin Setup

Main Plugin Class

BetterHudExample.java
package com.example.betterhudexample;

import kr.toxicity.hud.api.BetterHudAPI;
import kr.toxicity.hud.api.bukkit.BukkitBootstrap;
import kr.toxicity.hud.api.manager.PlaceholderManager;
import kr.toxicity.hud.api.manager.TriggerManager;
import kr.toxicity.hud.api.player.HudPlayer;
import kr.toxicity.hud.api.placeholder.HudPlaceholder;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;

import java.util.logging.Level;

public final class BetterHudExample extends JavaPlugin {
    
    private static BetterHudExample instance;
    
    @Override
    public void onEnable() {
        instance = this;
        
        // Check if BetterHud is loaded
        if (!getServer().getPluginManager().isPluginEnabled("BetterHud")) {
            getLogger().severe("BetterHud is not installed!");
            getServer().getPluginManager().disablePlugin(this);
            return;
        }
        
        try {
            // Register custom placeholders
            registerPlaceholders();
            
            // Register custom triggers
            registerTriggers();
            
            // Register event listeners
            getServer().getPluginManager().registerEvents(
                new PlayerListener(), 
                this
            );
            
            getLogger().info("BetterHud integration enabled!");
            
        } catch (Exception e) {
            getLogger().log(Level.SEVERE, "Failed to initialize BetterHud integration", e);
            getServer().getPluginManager().disablePlugin(this);
        }
    }
    
    @Override
    public void onDisable() {
        getLogger().info("BetterHud integration disabled!");
    }
    
    private void registerPlaceholders() {
        PlaceholderManager manager = BetterHudAPI.inst().getPlaceholderManager();
        
        // Register a number placeholder
        HudPlaceholder.<Number>builder()
            .function((args, event) -> player -> {
                // Return player's level
                return ((org.bukkit.entity.Player) player.handle()).getLevel();
            })
            .build()
            .add("player_level", manager.getNumberContainer());
        
        // Register a string placeholder
        HudPlaceholder.<String>builder()
            .function((args, event) -> player -> {
                org.bukkit.entity.Player bukkitPlayer = 
                    (org.bukkit.entity.Player) player.handle();
                return bukkitPlayer.getWorld().getName();
            })
            .build()
            .add("player_world", manager.getStringContainer());
        
        // Register a boolean placeholder
        HudPlaceholder.<Boolean>builder()
            .function((args, event) -> player -> {
                org.bukkit.entity.Player bukkitPlayer = 
                    (org.bukkit.entity.Player) player.handle();
                return bukkitPlayer.isFlying();
            })
            .build()
            .add("is_flying", manager.getBooleanContainer());
    }
    
    private void registerTriggers() {
        TriggerManager manager = BetterHudAPI.inst().getTriggerManager();
        BukkitBootstrap bootstrap = (BukkitBootstrap) BetterHudAPI.inst().getBootstrap();
        
        // Register custom event triggers
        // See PlayerListener class for event handling
    }
    
    public static BetterHudExample getInstance() {
        return instance;
    }
}

plugin.yml

plugin.yml
name: BetterHudExample
version: 1.0.0
main: com.example.betterhudexample.BetterHudExample
api-version: 1.21
depend: [BetterHud]
authors: [YourName]
description: Example BetterHud integration plugin

Working with Players

Getting HudPlayer Instance

import kr.toxicity.hud.api.BetterHudAPI;
import kr.toxicity.hud.api.manager.PlayerManager;
import kr.toxicity.hud.api.player.HudPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;

public class PlayerHelper {
    
    @Nullable
    public static HudPlayer getHudPlayer(Player player) {
        PlayerManager manager = BetterHudAPI.inst().getPlayerManager();
        return manager.getHudPlayer(player.getUniqueId());
    }
    
    public static void disableHud(Player player) {
        HudPlayer hudPlayer = getHudPlayer(player);
        if (hudPlayer != null) {
            hudPlayer.setHudEnabled(false);
        }
    }
    
    public static void enableHud(Player player) {
        HudPlayer hudPlayer = getHudPlayer(player);
        if (hudPlayer != null) {
            hudPlayer.setHudEnabled(true);
        }
    }
}

Custom Placeholders

Advanced Placeholder with Arguments

import kr.toxicity.hud.api.BetterHudAPI;
import kr.toxicity.hud.api.placeholder.HudPlaceholder;

public class CustomPlaceholders {
    
    public static void register() {
        var manager = BetterHudAPI.inst().getPlaceholderManager();
        
        // Placeholder with required arguments: <custom_stat_maxHealth>
        HudPlaceholder.<Number>builder()
            .requiredArgsLength(1)  // Require 1 argument
            .function((args, event) -> {
                String statName = args.get(0);  // Get the argument
                
                return player -> {
                    org.bukkit.entity.Player p = 
                        (org.bukkit.entity.Player) player.handle();
                    
                    // Return different values based on stat name
                    return switch (statName.toLowerCase()) {
                        case "maxhealth" -> p.getMaxHealth();
                        case "health" -> p.getHealth();
                        case "food" -> (double) p.getFoodLevel();
                        default -> 0.0;
                    };
                };
            })
            .build()
            .add("custom_stat", manager.getNumberContainer());
        
        // Placeholder using update event context
        HudPlaceholder.<String>builder()
            .function((args, event) -> player -> {
                // Access event data from UpdateEvent
                var variables = event.getVariableMap();
                return variables.getOrDefault("custom_data", "default");
            })
            .build()
            .add("event_data", manager.getStringContainer());
    }
}

Event Listeners

Custom Popup Events

PlayerListener.java
package com.example.betterhudexample;

import kr.toxicity.hud.api.bukkit.event.CustomPopupEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLevelChangeEvent;

public class PlayerListener implements Listener {
    
    @EventHandler
    public void onLevelUp(PlayerLevelChangeEvent event) {
        int oldLevel = event.getOldLevel();
        int newLevel = event.getNewLevel();
        
        if (newLevel > oldLevel) {
            // Create custom popup event
            CustomPopupEvent popupEvent = new CustomPopupEvent(
                event.getPlayer(),
                "level_up_popup"  // Must match popup name in BetterHud config
            );
            
            // Add custom variables for the popup
            popupEvent.getVariables().put("old_level", String.valueOf(oldLevel));
            popupEvent.getVariables().put("new_level", String.valueOf(newLevel));
            popupEvent.getVariables().put("gained", String.valueOf(newLevel - oldLevel));
            
            // Call the event
            event.getPlayer().getServer().getPluginManager().callEvent(popupEvent);
        }
    }
}

Showing Popups Programmatically

import kr.toxicity.hud.api.BetterHudAPI;
import kr.toxicity.hud.api.manager.PopupManager;
import kr.toxicity.hud.api.player.HudPlayer;
import kr.toxicity.hud.api.popup.Popup;
import kr.toxicity.hud.api.update.UpdateEvent;
import org.bukkit.entity.Player;

import java.util.HashMap;
import java.util.Map;

public class PopupHelper {
    
    public static void showPopup(Player player, String popupName, Map<String, String> variables) {
        PlayerManager playerManager = BetterHudAPI.inst().getPlayerManager();
        HudPlayer hudPlayer = playerManager.getHudPlayer(player.getUniqueId());
        
        if (hudPlayer == null) {
            return;  // Player not loaded
        }
        
        PopupManager popupManager = BetterHudAPI.inst().getPopupManager();
        Popup popup = popupManager.getPopup(popupName);
        
        if (popup == null) {
            player.sendMessage("Popup '" + popupName + "' not found!");
            return;
        }
        
        // Create update event with variables
        UpdateEvent event = UpdateEvent.builder()
            .variables(variables != null ? variables : new HashMap<>())
            .build();
        
        // Show the popup
        try {
            popup.show(hudPlayer, event);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // Example usage
    public static void showDamagePopup(Player player, double damage) {
        Map<String, String> vars = new HashMap<>();
        vars.put("damage", String.format("%.1f", damage));
        vars.put("damage_type", "physical");
        
        showPopup(player, "damage_indicator", vars);
    }
}

Compass Points

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 org.bukkit.Location;
import org.bukkit.entity.Player;

public class CompassHelper {
    
    // Add a compass point for a specific player
    public static void addCompassPoint(Player player, Location location, String name, String icon) {
        PlayerManager manager = BetterHudAPI.inst().getPlayerManager();
        HudPlayer hudPlayer = manager.getHudPlayer(player.getUniqueId());
        
        if (hudPlayer != null) {
            // Convert Bukkit Location to LocationWrapper
            LocationWrapper wrapper = LocationWrapper.of(
                location.getWorld().getName(),
                location.getX(),
                location.getY(),
                location.getZ()
            );
            
            PointedLocation pointedLocation = PointedLocation.builder()
                .location(wrapper)
                .name(name)
                .icon(icon)
                .build();
            
            hudPlayer.pointers().add(pointedLocation);
        }
    }
    
    // Remove compass point by name
    public static void removeCompassPoint(Player player, String name) {
        PlayerManager manager = BetterHudAPI.inst().getPlayerManager();
        HudPlayer hudPlayer = manager.getHudPlayer(player.getUniqueId());
        
        if (hudPlayer != null) {
            hudPlayer.pointers().removeIf(p -> p.getName().equals(name));
        }
    }
    
    // Add global compass points for all players
    public static void registerGlobalLocationProvider() {
        PlayerManager manager = BetterHudAPI.inst().getPlayerManager();
        
        manager.addLocationProvider(new PointedLocationProvider() {
            @Override
            public Set<PointedLocation> provide(HudPlayer player) {
                // Return a set of locations for this player
                Set<PointedLocation> locations = new HashSet<>();
                
                // Example: Add spawn point
                Player bukkitPlayer = (Player) player.handle();
                Location spawn = bukkitPlayer.getWorld().getSpawnLocation();
                
                locations.add(PointedLocation.builder()
                    .location(LocationWrapper.of(
                        spawn.getWorld().getName(),
                        spawn.getX(),
                        spawn.getY(),
                        spawn.getZ()
                    ))
                    .name("Spawn")
                    .icon("spawn")
                    .build());
                
                return locations;
            }
        });
    }
}

Error Handling

import kr.toxicity.hud.api.BetterHudAPI;
import org.bukkit.entity.Player;

import java.util.logging.Level;
import java.util.logging.Logger;

public class SafeHudOperations {
    
    private static final Logger LOGGER = Logger.getLogger("BetterHudExample");
    
    public static void safeShowPopup(Player player, String popupName) {
        try {
            var playerManager = BetterHudAPI.inst().getPlayerManager();
            var hudPlayer = playerManager.getHudPlayer(player.getUniqueId());
            
            if (hudPlayer == null) {
                LOGGER.warning("HudPlayer not found for " + player.getName());
                return;
            }
            
            var popupManager = BetterHudAPI.inst().getPopupManager();
            var popup = popupManager.getPopup(popupName);
            
            if (popup == null) {
                LOGGER.warning("Popup '" + popupName + "' not found!");
                return;
            }
            
            popup.show(hudPlayer, UpdateEvent.builder().build());
            
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Failed to show popup to " + player.getName(), e);
        }
    }
}

Next Steps

Custom HUD

Learn how to create custom HUD elements

Custom Popup

Create dynamic popup notifications

Bukkit API Reference

Complete Bukkit API documentation

Standard API

Core API reference

Build docs developers (and LLMs) love