Skip to content

Boss bars, titles and action bars

Titles and action bars are fire and forget: nothing to create, nothing to release.

Welcome.cs
using Mc.Scripting;
using Mc.Scripting.Text;
namespace Welcome;
public sealed class PluginMain : Plugin
{
protected override void OnEnable()
{
Events.On((ref PlayerJoinEvent e) =>
{
e.Player.ShowTitle(
Component.Text("Welcome").Color(TextColor.Gold),
Component.Text("to the server").Color(TextColor.Gray),
new TitleTimes(FadeIn: 10, Stay: 60, FadeOut: 20)); // ticks: 20 = 1 second
e.Player.SendActionBar(Component.Legacy("&aType /help to get started"));
});
}
}
  • ShowTitle(title, subtitle, times) takes a Component for each line. The subtitle is optional (null for none). times is a TitleTimes in ticks (fade in, stay, fade out); null keeps the client’s current timings, which default to 10, 70 and 20. TitleTimes.From(fadeIn, stay, fadeOut) takes TimeSpan values instead.
  • ShowSubtitle(subtitle) sets a subtitle on its own; it appears with the next title.
  • player.ClearTitle() hides a title that is showing. ClearTitle(resetTimes: true) also restores the default timings.
  • The action bar is the single line above the hotbar. Sending again replaces it; it fades by itself after a few seconds, so resend it from a repeating task if you want it to stay.
  • Text is a Component. A plain string is literal text; use Component.Legacy("&a...") or Component.Parse("<green>...") explicitly for formatting, so user-controlled strings never inject formatting.

A BossBar is an owned resource: create it with BossBars.Create(...) on your plugin. It is removed from every viewer when you call Remove() (or Dispose()) and when your plugin unloads.

Member Meaning
BossBars.Create(string name, Component title, BossBarColor color, BossBarStyle style, float progress) creates a bar with no viewers yet; name is unique within the plugin (letters, digits, _ - . /)
bar.Progress 0.0 to 1.0 (values outside are clamped)
bar.Title, bar.Color, bar.Style change while it is shown
bar.Flags BossBarFlags: darken the sky, play boss music, create fog
bar.Visible hide and show the bar without losing its viewers
bar.AddViewer(Player) / bar.RemoveViewer(Player) who sees it (both also take a profile id, Uuid)
bar.Remove() removes it from every viewer

BossBarColor is Pink, Blue, Red, Green, Yellow, Purple or White. BossBarStyle is Solid, Segmented6, Segmented10, Segmented12 or Segmented20.

A repeating task shrinks the bar; at zero the bar is removed and a message is sent. /countdown is a Brigadier command; see commands with arguments.

Countdown.cs
using Mc.Scripting;
using Mc.Scripting.Text;
namespace Countdown;
public sealed class PluginMain : Plugin
{
private BossBar? _bar;
private ScheduledTask? _task;
protected override void OnEnable()
{
Commands.Register(Command.Literal("countdown")
.Permission("countdown.use")
.Then(Command.Argument("seconds", Arg.Int(1, 3600))
.Executes(ctx => Start(ctx.GetInt("seconds")))));
Events.On((ref PlayerJoinEvent e) => _bar?.AddViewer(e.Player));
}
private int Start(int seconds)
{
Stop();
int total = seconds * 20, left = total;
BossBar bar = BossBars.Create("countdown", Component.Legacy($"&eRestart in {seconds}s"),
BossBarColor.Yellow, BossBarStyle.Solid, 1f);
foreach (Player p in Server.Players) bar.AddViewer(p);
_bar = bar;
_task = Scheduler.RunEvery(20, () =>
{
left -= 20;
if (left <= 0)
{
Server.Broadcast("Time is up!");
Stop();
return;
}
bar.Progress = (float)left / total;
bar.Title = Component.Legacy($"&eRestart in {left / 20}s");
if (left <= 100) bar.Color = BossBarColor.Red;
});
return seconds;
}
private void Stop()
{
_task?.Cancel();
_bar?.Remove();
_task = null;
_bar = null;
}
protected override void OnDisable() => Stop(); // not required: unload removes the bar and cancels the task
}
  • Viewers who join later: a viewer is kept by profile id, and AddViewer also accepts the id of an offline player (the bar appears when he joins). The countdown adds joining players on PlayerJoinEvent, so new players see the bar.
  • One bar per purpose: boss bars stack at the top of the screen (the client shows several). Reuse one bar and change its title and progress rather than creating a bar per update. BossBars.Create throws InvalidOperationException when the name is already in use, so Remove() the old bar first.
  • Updating Progress, Title or Color sends a small packet to the viewers; set them only when the value changes.
  • Persistence: bars are not saved with the world.
  • Hot reload: a new version that creates a bar with the same name takes over the old one, with its viewers. A bar the new version does not create is removed when the old version unloads.