Message
The message module provides a Kotlin DSL for building Adventure components in a declarative, type-safe manner. The Paper integration adds extension functions for sending messages directly to players and command senders.
Modules
| Module | Description |
|---|---|
kraftlin-message-core |
DSL for building Adventure components + Audience.message extensions |
kraftlin-message-paper |
(Deprecated) Legacy CommandSender.message — use core's Audience.message instead |
kraftlin-message-bungee |
BungeeCord CommandSender.message extensions via BungeeComponentSerializer |
kraftlin-message-velocity |
Convenience dependency — re-exports core (Velocity natively supports Adventure) |
Getting Started
Build a simple message:
val msg = message {
text("Hello ")
text("World!") {
color(NamedTextColor.GOLD)
bold()
}
}
With Paper integration, send messages directly:
player.message {
text("Click here") {
color(NamedTextColor.GREEN)
runCommand("/help")
hoverMessage("Get help")
}
}
Core Concepts
Component Building
Messages are built using nested DSL blocks:
message {
text("Part 1")
text("Part 2") {
color(NamedTextColor.BLUE)
}
newLine()
text("Part 3")
}
Formatting Inheritance
Base formatting applies to all components unless overridden:
message {
color(NamedTextColor.YELLOW) // Base color
bold() // Base decoration
text("Inherits yellow + bold")
text("Custom color") {
color(NamedTextColor.GRAY) // Overrides base
// Still bold
}
text("Back to yellow + bold")
}
Interactive Components
Add click and hover actions:
message {
text("Execute command") {
runCommand("/say hello")
hoverMessage("Click to say hello")
}
text("Open link") {
openUrl("https://example.com")
underlined()
}
}
Text Components
Basic Text
text("Simple text")
text("Formatted text") {
color(NamedTextColor.RED)
bold()
italic()
}
Color Shorthand
text("Colored", NamedTextColor.BLUE)
Convenience Methods
message {
text("Line 1")
newLine() // Add line break
text("Line 2")
space() // Add space
text("after space")
}
Styling
Colors
text("Text") {
color(NamedTextColor.GOLD)
// or
color(TextColor.color(0xFF5733))
}
Decorations
All standard text decorations are supported:
text("Formatted") {
bold()
italic()
underlined()
strikeThrough()
obfuscated()
}
Click Events
Only one click action per component (mutually exclusive):
// Execute command as the clicking player
text("Execute") { runCommand("/gamemode creative") }
// Insert command into chat bar (doesn't execute)
text("Suggest") { suggestCommand("/msg ") }
// Open URL dialog
text("Link") { openUrl("https://example.com") }
// Copy text to clipboard
text("Copy") { copyToClipboard("Copied text") }
Hover Events
Only one hover event per component:
Simple Text Hover
text("Hover me") {
hoverMessage("Simple tooltip")
}
Formatted Hover
text("Hover me") {
hoverMessage("Tooltip text") {
color(NamedTextColor.GOLD)
bold()
}
}
Multi-Component Hover
text("Hover me") {
hoverMessage {
text("Line 1") { color(NamedTextColor.GOLD) }
newLine()
text("Line 2") { color(NamedTextColor.GRAY) }
}
}
Custom Hover Events
text("Item") {
hoverEvent(itemStack.asHoverEvent())
}
Insertion
Text inserted into chat on shift+click (can be combined with other actions):
text("Insert text") {
insert("This text is inserted")
runCommand("/help") // Can combine with click action
}
Legacy Support
Converting Legacy Text
Convert §-style formatted text to components:
message {
legacyText("§aGreen §bBlue §lbold")
}
Converting to Legacy
val component: Component = message { /* ... */ }
val legacy: String = component.toLegacyMessage()
Existing Components
Wrap existing Adventure components:
val existingComponent: Component = Component.text("Existing")
message {
text(existingComponent) {
bold() // Add additional formatting
}
}
Sending Messages
Audience Extensions (all platforms)
The core module provides Audience.message extensions that work on any platform
whose sender implements Adventure's Audience interface (Paper, Velocity, and any other):
audience.message {
text("Hello ") { color(NamedTextColor.GREEN) }
text("World!")
}
audience.message("Simple text")
audience.message("Colored text", NamedTextColor.BLUE)
BungeeCord
BungeeCord does not implement Audience, so kraftlin-message-bungee provides
CommandSender.message extensions that serialize via BungeeComponentSerializer:
sender.message {
text("Hello from BungeeCord!") { color(NamedTextColor.GOLD) }
}
Paper (Legacy)
The Paper module's CommandSender.message extensions are deprecated.
Use Audience.message from the core module instead — Paper's CommandSender implements Audience.
Extension functions on CommandSender:
// Build and send complex message
player.message {
text("Hello ") { color(NamedTextColor.GREEN) }
text("World!")
}
// Send simple text
player.message("Simple message")
// Send colored text
player.message("Colored", NamedTextColor.BLUE)
// Send single component with formatting
player.message("Click me") {
runCommand("/help")
hoverMessage("Get help")
}
// Send existing component
player.message(existingComponent) {
bold() // Optional additional formatting
}
Advanced Examples
Command Feedback
player.message {
text("Teleported to ") { color(NamedTextColor.GRAY) }
text("${target.name}") {
color(NamedTextColor.GOLD)
hoverMessage {
text("Location: ")
text("${target.location.blockX}, ${target.location.blockY}, ${target.location.blockZ}")
}
}
}
Interactive Menu
player.message {
text("[Accept]") {
color(NamedTextColor.GREEN)
runCommand("/quest accept ${questId}")
hoverMessage("Click to accept quest")
}
space()
text("[Decline]") {
color(NamedTextColor.RED)
runCommand("/quest decline ${questId}")
hoverMessage("Click to decline quest")
}
}
Formatted List
message {
color(NamedTextColor.GRAY)
text("Players online:")
newLine()
players.forEach { player ->
text("• ${player.name}") {
color(NamedTextColor.WHITE)
hoverMessage {
text("Click to teleport")
}
runCommand("/tp ${player.name}")
}
newLine()
}
}
Best Practices
-
Use formatting inheritance for consistent styling:
message { color(NamedTextColor.GRAY) // Set base color once text("Part 1") text("Part 2") } -
Use
Audience.messagefor sending messages:player.message { /* ... */ } // ✓ Clean and direct player.sendMessage(message { /* ... */ }) // ✗ Verbose -
Use hover messages for additional context:
text("Hover for details") { hoverMessage("Additional information here") } -
Validate actions - The DSL will throw exceptions if you try to add multiple conflicting actions
Why Kraftlin Messages?
Benefits over raw Adventure builders:
Without Kraftlin:
Component.text()
.content("Click me")
.color(NamedTextColor.GREEN)
.clickEvent(ClickEvent.runCommand("/help"))
.hoverEvent(HoverEvent.showText(Component.text("Help")))
.build()
With Kraftlin:
message {
text("Click me") {
color(NamedTextColor.GREEN)
runCommand("/help")
hoverMessage("Help")
}
}
Benefits: - Declarative DSL matches structure of message - Cleaner, more readable syntax - Formatting inheritance reduces repetition - Type-safe at compile time - Easy composition of multi-component messages - Validation prevents conflicting actions