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