Messaging
Chat, action bars, titles and boss bars with placeholders, MiniMessage and legacy colour codes on every version.
bloom.messages() returns a Messenger. Messengers are immutable: withPrefix, withFormat and
withUnsupportedPolicy return new ones, so keep the variants you use as fields.
Sending messages
Messenger messages = bloom.messages().withPrefix("&d&lShop &8ยป &7");
messages.send(
player,
"You bought &f{amount}x {item}&7.",
Placeholder.of("amount", 3),
Placeholder.of("item", "Golden Apple"));
messages.broadcast("&f{player} &7joined the arena!", Placeholder.of("player", player.getName()));send adds the messenger's prefix; action bars, titles and boss bars never get the prefix. broadcast sends to every
online player and the console.
Placeholders
Write {name} in a template and pass Placeholder.of("name", value). Substitution happens in one pass, so a value
that itself contains {other} is not expanded again. Unknown placeholders are left as they are.
Placeholder values are plain text by default: colour codes or MiniMessage tags inside them are neutralised. That
makes it safe to insert player names and chat. Use Placeholder.formatted only for text you trust.
// Placeholder.of: plain text. "&c" or "<red>" inside chatMessage is shown literally.
bloom.messages().send(player, "&7[Chat] &f{message}", Placeholder.of("message", chatMessage));
// Placeholder.formatted: trusted text that should keep its formatting.
bloom.messages().send(player, "Rank: {rank}", Placeholder.formatted("rank", "&6&lVIP"));Formats
Bloom supports two markup formats on every server. The default is TextFormat.LEGACY; change it for the whole plugin
with BloomOptions.textFormat(...) or per messenger with withFormat.
LEGACY | MINIMESSAGE | |
|---|---|---|
| Example | &dHello &lworld | <light_purple>Hello <bold>world |
| Paper 1.21+ / 26.x | Converted with Adventure, &#RRGGBB hex supported | Real MiniMessage |
| 1.7 / 1.8 | Native | Converted by Bloom (see below) |
Messenger mini = bloom.messages().withFormat(TextFormat.MINIMESSAGE);
mini.send(
player,
"<gradient:#F2A7CC:#B9A0DD>Welcome</gradient> <gray>to the server, <white>{player}</white>!",
Placeholder.of("player", player.getName()));
// On 1.7/1.8 the gradient uses its first colour; colours, bold, italic and so on convert exactly.Action bars and titles
Messenger messages = bloom.messages();
messages.actionBar(player, "&a+5 coins");
messages.title(
player,
Title.of("&dRound 2", "&7Fight!")
.times(Duration.ofMillis(250), Duration.ofSeconds(2), Duration.ofMillis(500)));
// Returns false when skipped (e.g. action bars on 1.7.10 with the default SKIP policy).
boolean shown = messages.actionBar(player, "&eCapturing...");Title timings need the TITLE_TIMINGS capability; on 1.8 the client's default timings are used.
Boss bars
Boss bars exist only on modern Paper (BOSS_BAR). They must be created and changed on the main thread, and Bloom
hides them from all viewers on shutdown.
BloomBossBar bar = bloom.messages()
.bossBar(
"&dEvent ends in {time}",
1.0f,
BossBarColor.PINK,
BossBarStyle.SEGMENTED_10,
Placeholder.of("time", "5:00"));
bar.show(player);
bar.progress(0.5f).title("&dEvent ends in 2:30");
bar.close(); // also closed automatically on shutdown
// isFunctional() is false on servers without boss bars (unless the policy is THROW, which throws instead)Unsupported features
When a server lacks a feature, the messenger's UnsupportedFeaturePolicy decides what happens:
| Policy | Effect |
|---|---|
SKIP (default) | Nothing is sent; the method returns false; one warning per feature is logged |
FALLBACK_TO_CHAT | Action bars and titles are sent as chat messages; boss bars are skipped |
THROW | UnsupportedCapabilityException naming the capability and the platform |
Messenger strict = bloom.messages().withUnsupportedPolicy(UnsupportedFeaturePolicy.THROW);
Messenger lenient = bloom.messages().withUnsupportedPolicy(UnsupportedFeaturePolicy.FALLBACK_TO_CHAT);
lenient.title(player, Title.of("&6Victory")); // a chat line on servers without titlesThreads
Chat, action bars and titles can be sent from any thread. Boss bars are main-thread only. For strings that must be
plain legacy text (scoreboards, signs), use messenger.legacy(template, placeholders...).