Boss bars

Show a boss bar at the top of players' screens and drive its progress.

A boss bar is the coloured bar that stretches across the top of the screen - the one you see while fighting the Ender Dragon. It carries a title, a colour, a segment style, and a progress value from 0.0 to 1.0, and you choose which players see it. It's the natural fit for anything time-based or global: an event countdown, a raid timer, a server-wide objective.

Create and show a bar

Ask the server for a bar, then attach the players who should see it. A bar with no players attached simply isn't drawn:

include/my_plugin.h
#include <endstone/endstone.hpp>

namespace es = endstone;

auto bar = getServer().createBossBar("Event starting", es::BarColor::Blue, es::BarStyle::Solid);
bar->addPlayer(player);

createBossBar returns a std::unique_ptr<BossBar> and starts the bar full (getProgress() == 1.0). BarColor covers the eight client colours; BarStyle is either Solid or one of the Segmented6 / Segmented10 / Segmented12 / Segmented20 notched styles.

Drive the progress

The bar earns its keep when you update it over time. Hold onto the instance and change its progress, title, or colour from a scheduled task. This plugin runs a ten-second countdown, draining the bar one tick at a time and flipping it when time's up:

include/my_plugin.h
#include <endstone/endstone.hpp>

namespace es = endstone;

class MyPlugin : public es::Plugin {
public:
    void onEnable() override
    {
        bar_ = getServer().createBossBar("Raid in 10s", es::BarColor::Red, es::BarStyle::Segmented10);
        bar_->addFlag(es::BarFlag::DarkenSky);  // dim the sky like a real boss fight
        for (auto &player : getServer().getOnlinePlayers()) {
            bar_->addPlayer(*player);
        }

        remaining_ = kTotal;
        task_ = getServer().getScheduler().runTaskTimer(*this, [this]() { countdown(); }, 0, 1);
        registerEvent(&MyPlugin::onPlayerJoin, *this);
    }

    void onPlayerJoin(es::PlayerJoinEvent &event)
    {
        bar_->addPlayer(event.getPlayer());  // late joiners see it too
    }

    void countdown()
    {
        remaining_ -= 1;
        bar_->setProgress(remaining_ > 0 ? static_cast<float>(remaining_) / kTotal : 0.0f);
        if (remaining_ <= 0) {
            bar_->setTitle("Raid!");
            bar_->setColor(es::BarColor::Purple);
            task_->cancel();
        }
    }

    void onDisable() override
    {
        bar_->removeAll();
    }

private:
    static constexpr int kTotal = 200;  // ticks, about ten seconds
    std::unique_ptr<es::BossBar> bar_;
    std::shared_ptr<es::Task> task_;
    int remaining_ = 0;
};

A few details worth knowing:

  • setProgress expects a value in [0.0, 1.0] - clamp it yourself as the example does so a stray value never slips out of range.
  • Flags add atmosphere. BarFlag::DarkenSky darkens the sky and BarFlag::CreateFog rolls in fog; add and remove them with addFlag / removeFlag.
  • Attachment is per player. addPlayer and removePlayer control exactly who sees the bar; removeAll clears everyone, which is the tidy thing to do in onDisable. You can also hide a bar from all its viewers without detaching them with setVisible(false).

A bar per player

The example above shares one bar across everyone. For a value that differs per player - personal stamina, a quest timer - create a separate bar for each and keep them in a map keyed by the player's getUniqueId(), which stays stable even if they rename:

// std::unordered_map<es::UUID, std::unique_ptr<es::BossBar>> bars_;

void showStamina(es::Player &player, float fraction)
{
    auto it = bars_.find(player.getUniqueId());
    if (it == bars_.end()) {
        auto bar = getServer().createBossBar("Stamina", es::BarColor::Green, es::BarStyle::Solid);
        bar->addPlayer(player);
        it = bars_.emplace(player.getUniqueId(), std::move(bar)).first;
    }
    it->second->setProgress(fraction);
}

Drop each bar when its player leaves, so you're not holding bars for people who are gone:

void onPlayerQuit(es::PlayerQuitEvent &event)
{
    auto it = bars_.find(event.getPlayer().getUniqueId());
    if (it != bars_.end()) {
        it->second->removeAll();
        bars_.erase(it);
    }
}

On this page