CenterMessage
Centers chat messages. Mix legacy & and §, hex &#RRGGBB, Spigot hex §x§R§R§G§G§B§B and
MiniMessage tags in the same string: color codes never push the text off center, only visible glyphs
count toward the padding. Static facade, no onEnable hook, no config files.
| Minecraft | 1.16+ |
| Java | 8 bytecode |
| Platforms | Spigot, Paper, Folia, BungeeCord, Velocity |
| Author | Senkex |
| Source | senkex/CenterMessage |
The centering engine is dependency free. Legacy and hex input is measured and padded with pure
java.*, so CenterMessage.center(...) runs with nothing on the classpath. Adventure and
MiniMessage load lazily, only when you use a tag or the Component API.
Relocate the package when you bundle it. Two plugins shading different versions of
com.github.senkex.centermessage will clash on the classpath. See Shading.
Install
Gradle (Kotlin)
repositories {
maven("https://jitpack.io")
}
dependencies {
implementation("com.github.senkex:CenterMessage:version")
}Bukkit, PlaceholderAPI and Adventure are compileOnly. They are not transitive and never reach your
POM.
| Feature | Needs at runtime |
|---|---|
CenterMessage, CenterHead, CenterOptions | nothing, pure java.* |
MiniMessage tags, CenterMessageComponents | adventure-api, adventure-text-minimessage, adventure-text-serializer-legacy |
CenterMessageBukkit | the Spigot or Paper server it runs on |
| PlaceholderAPI expansion | PlaceholderAPI installed, resolved lazily |
Paper and Velocity ship Adventure. On Spigot and BungeeCord, shade the matching
adventure-platform-* bridge yourself.
Usage
The zero dependency core. It never touches Adventure, so it works everywhere.
String line = CenterMessage.center("&#FF55AA Centered <ext");
player.sendMessage(line);Multi line and <center> blocks are string level too.
String block = CenterMessage.processCenterTags(
"<center>&6&lWelcome</center>\n" +
"&7Use /help to see commands.\n" +
"<center>&7Have fun.</center>");Components and audiences
CenterMessageComponents is the only class that pulls in Adventure, loaded lazily. Use it for
Component results, hover and click decoration, or sending straight to an Audience.
CenterMessageComponents.send(audience, "&aHello &b<bold>World</bold>");
Component comp = CenterMessageComponents.centerComponent("<gradient:#f00:#0f0>Hi</gradient>");
player.sendMessage(comp);
// Re center an existing component, keeping its click and hover events
Component custom = Component.text("hello").color(NamedTextColor.AQUA);
player.sendMessage(CenterMessageComponents.centerComponent(custom));
CenterMessageComponents.sendBlock(audience, "<center>&6&lWelcome</center>\n&7Use /help");On Bukkit, pass a CommandSender straight into the Bukkit facade. It uses Adventure when the sender
supports it and falls back to legacy sendMessage otherwise.
CenterMessageBukkit.send(player, "&aHello &b<bold>World</bold>");
CenterMessageBukkit.sendBlock(player, "<center>&6&lWelcome</center>\n&7Use /help");Options
The default is chat width, CenterOptions.CHAT, 154 px. For signs, books, holograms or custom UIs,
set the half width through the builder.
CenterOptions sign = CenterOptions.builder().centerPx(45).build();
CenterMessage.center("&aTitle", sign);
CenterOptions custom = CenterOptions.builder()
.centerPx(80)
.parseMiniMessage(false)
.build();
String centered = CenterMessage.center("&aTitle", custom);Every centering method on CenterMessage, CenterMessageComponents and CenterMessageBukkit takes
an optional CenterOptions.
Heads
CenterHead centers a rendered head next to text. It never imports HeadRender: feed it the
List<String> that HeadRender.render(...) returns plus the
text for each row.
HeadRender.render("Senkex").thenAccept(head -> {
List<String> text = List.of("", "&fSenkex", "&7VIP", "", "&eCoins: 1200", "", "", "");
// Type 1, everything centered, head included
CenterHead.card(head, text).forEach(player::sendMessage);
// Type 2, head stays put, text centered beside it
CenterHead.beside(head, text).forEach(player::sendMessage);
});Head width is measured through FontInfo.BLOCK_WIDTH, the same value HeadRender uses, so both
libraries agree.
PlaceholderAPI
PlaceholderAPI is Bukkit only, so it lives in CenterMessageBukkit. If PAPI is installed, every
method there that receives a Player or OfflinePlayer expands placeholders before parsing. No
hard dependency is needed.
CenterMessageBukkit.send(player, "&6Balance: &a%vault_eco_balance%");Colorize
CenterMessage.colorize is a drop in replacement for ChatColor.translateAlternateColorCodes that
also understands &#RRGGBB and MiniMessage tags.
String colored = CenterMessage.colorize("&aHello &#FF55AA world <bold>!</bold>");Notes
- Pipeline order:
&#RRGGBB, then&x, then legacy to MiniMessage conversion, then MiniMessage parse. Mixing formats in the same string is fine. PlaceholderAPI expansion runs before all of it when going throughCenterMessageBukkit. - Width is measured against the vanilla font. Resource packs can override widths with
FontInfo.registerWidth(char, int). - Bold is honored end to end. Nested
<bold>and legacy§lboth add the correct extra pixel per character when measuring.
Migrating from 2.x
In 2.x the facade imported org.bukkit.*, so it could not load on BungeeCord or Velocity. 3.0.0
splits it: CenterMessage keeps every platform neutral method, the Bukkit typed ones moved to
CenterMessageBukkit unchanged.
| 2.x | 3.0.0 |
|---|---|
CenterMessage.send(CommandSender, ...) | CenterMessageBukkit.send(CommandSender, ...) |
CenterMessage.sendBlock(CommandSender, ...) | CenterMessageBukkit.sendBlock(CommandSender, ...) |
CenterMessage.broadcast(...) | CenterMessageBukkit.broadcast(...) |
CenterMessage.center(String, CenterOptions, OfflinePlayer) | CenterMessageBukkit.center(...) |
CenterMessage.centerComponent(String, CenterOptions, OfflinePlayer) | CenterMessageBukkit.centerComponent(...) |
CenterMessage.parse(String, CenterOptions, OfflinePlayer) | CenterMessageBukkit.parse(...) |
The Adventure typed methods moved off CenterMessage as well.
| Before | Now |
|---|---|
CenterMessage.centerComponent(...) | CenterMessageComponents.centerComponent(...) |
CenterMessage.parse(...) | CenterMessageComponents.parse(...) |
CenterMessage.send(Audience, ...) | CenterMessageComponents.send(Audience, ...) |
CenterMessage.sendBlock(Audience, ...) | CenterMessageComponents.sendBlock(Audience, ...) |
The string methods center, centerLines, processCenterTags, colorize, translateAmp and
translateAmpHex keep working untouched, and now work on proxies too.
Shading
Gradle (Kotlin)
plugins {
id("com.gradleup.shadow") version "8.3.5"
}
tasks {
shadowJar {
relocate("com.github.senkex.centermessage", "my.plugin.libs.centermessage")
}
}Change my.plugin.libs.centermessage to a package inside your own plugin. The shaded jar in
build/libs/ or target/ is what you ship.