Skip to content

Commit 7043a49

Browse files
CopilotTrilleo
andauthored
Feature: Add MessageUtil with sendPrefixed extension for CommandSender
Agent-Logs-Url: https://github.com/Trilleo/PaperExamplePlugin/sessions/f8d52b5d-9d4e-4e36-b9d0-e87c3ef954bd Co-authored-by: Trilleo <133519132+Trilleo@users.noreply.github.com>
1 parent a33a9db commit 7043a49

6 files changed

Lines changed: 189 additions & 7 deletions

File tree

docs/UTILITY_GUIDE.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ reduce boilerplate and provide commonly needed functionality out of the box.
99
| `CountdownUtil` | Per-player countdown with configurable display and sound |
1010
| `TeamUtil` | Custom team management with server-data persistence |
1111
| `TagUtil` | Per-player string tag management with player-data persistence |
12+
| `MessageUtil` | Prefix-decorated message sender for players and command senders |
1213

1314
---
1415

@@ -356,3 +357,81 @@ fun onEnterVipArea(player: Player) {
356357
```
357358

358359
---
360+
361+
## MessageUtil
362+
363+
`MessageUtil` sends prefix-decorated messages to players and command senders. The prefix is
364+
read from `config.yml` under the `message-prefix` key and supports both plain text and
365+
[MiniMessage](https://docs.advntr.dev/minimessage/index.html) formatting. The plugin
366+
initialises `MessageUtil` automatically at startup and after every `/exampleplugin reload`,
367+
so no manual setup is required in your own commands or listeners.
368+
369+
### Configuration
370+
371+
```yaml
372+
# config.yml
373+
374+
# Plain text
375+
message-prefix: "[ExamplePlugin]"
376+
377+
# MiniMessage (rich formatting)
378+
message-prefix: "<gray>[<gold>ExamplePlugin<gray>]"
379+
```
380+
381+
### Usage
382+
383+
```kotlin
384+
import com.example.exampleplugin.utils.sendPrefixed
385+
386+
// Plain text
387+
player.sendPrefixed("Hello!")
388+
389+
// MiniMessage
390+
player.sendPrefixed("<green>Operation successful!")
391+
player.sendPrefixed("<red>Something went wrong.")
392+
393+
// Adventure Component
394+
import net.kyori.adventure.text.Component
395+
import net.kyori.adventure.text.format.NamedTextColor
396+
397+
player.sendPrefixed(Component.text("Hello!", NamedTextColor.GREEN))
398+
399+
// Works on any CommandSender (console, players, command blocks)
400+
sender.sendPrefixed("Configuration reloaded!")
401+
```
402+
403+
### Methods
404+
405+
| Method / Extension | Description |
406+
|:----------------------------------------|:-------------------------------------------------------------------|
407+
| `MessageUtil.init(prefixString)` | Loads the prefix (plain text or MiniMessage). Called automatically at startup and on reload. |
408+
| `MessageUtil.sendPrefixed(sender, msg: String)` | Sends a plain-text or MiniMessage string with the prefix prepended. |
409+
| `MessageUtil.sendPrefixed(sender, msg: Component)` | Sends an Adventure `Component` with the prefix prepended. |
410+
| `CommandSender.sendPrefixed(msg: String)` | Extension shorthand for `MessageUtil.sendPrefixed(this, msg)`. |
411+
| `CommandSender.sendPrefixed(msg: Component)` | Extension shorthand for `MessageUtil.sendPrefixed(this, msg)`. |
412+
413+
### Example (Command)
414+
415+
```kotlin
416+
import com.example.exampleplugin.registration.PluginCommand
417+
import com.example.exampleplugin.utils.sendPrefixed
418+
import net.kyori.adventure.text.Component
419+
import net.kyori.adventure.text.format.NamedTextColor
420+
import org.bukkit.command.CommandSender
421+
422+
class GreetCommand : PluginCommand(
423+
name = "greet",
424+
description = "Send a greeting"
425+
) {
426+
override fun execute(sender: CommandSender, args: Array<out String>): Boolean {
427+
// String (MiniMessage)
428+
sender.sendPrefixed("<green>Welcome to the server!")
429+
430+
// Component
431+
sender.sendPrefixed(Component.text("Hello!", NamedTextColor.GOLD))
432+
return true
433+
}
434+
}
435+
```
436+
437+
---

src/main/kotlin/com/example/exampleplugin/Main.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.example.exampleplugin.config.PluginConfig
44
import com.example.exampleplugin.data.PlayerDataManager
55
import com.example.exampleplugin.data.ServerDataManager
66
import com.example.exampleplugin.registration.*
7+
import com.example.exampleplugin.utils.MessageUtil
78
import org.bukkit.plugin.java.JavaPlugin
89

910
class Main : JavaPlugin() {
@@ -16,6 +17,7 @@ class Main : JavaPlugin() {
1617
// Load configuration
1718
logger.info("Loading configuration...")
1819
pluginConfig = PluginConfig(this)
20+
MessageUtil.init(pluginConfig.messagePrefix)
1921

2022
// Initialise data managers
2123
logger.info("Initialising data managers...")

src/main/kotlin/com/example/exampleplugin/commands/moderation/ReloadCommand.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package com.example.exampleplugin.commands.moderation
22

33
import com.example.exampleplugin.Main
44
import com.example.exampleplugin.registration.PluginCommand
5+
import com.example.exampleplugin.utils.MessageUtil
6+
import com.example.exampleplugin.utils.sendPrefixed
57
import org.bukkit.command.CommandSender
68
import org.bukkit.plugin.java.JavaPlugin
79

@@ -19,11 +21,12 @@ class ReloadCommand(private val plugin: JavaPlugin) : PluginCommand(
1921
override fun execute(sender: CommandSender, args: Array<out String>): Boolean {
2022
val main = plugin as? Main
2123
if (main == null) {
22-
sender.sendMessage("Error: Plugin instance type mismatch. Unable to reload configuration.")
24+
sender.sendPrefixed("<red>Error: Plugin instance type mismatch. Unable to reload configuration.")
2325
return true
2426
}
2527
main.pluginConfig.reload()
26-
sender.sendMessage("Configuration reloaded!")
28+
MessageUtil.init(main.pluginConfig.messagePrefix)
29+
sender.sendPrefixed("<green>Configuration reloaded!")
2730
return true
2831
}
2932
}

src/main/kotlin/com/example/exampleplugin/config/PluginConfig.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ class PluginConfig(private val plugin: JavaPlugin) {
3333
config = plugin.config
3434
}
3535

36+
// ── Plugin Properties ────────────────────────────────────────────────
37+
38+
/**
39+
* The prefix shown before plugin messages. Taken from the `message-prefix`
40+
* key in `config.yml`. Supports plain text and MiniMessage formatting.
41+
*/
42+
val messagePrefix: String
43+
get() = getString("message-prefix", "[ExamplePlugin]")
44+
3645
// ── Typed Getters ───────────────────────────────────────────────────
3746

3847
/**

src/main/kotlin/com/example/exampleplugin/registration/CommandRegistrar.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.example.exampleplugin.registration
22

3+
import com.example.exampleplugin.utils.sendPrefixed
34
import org.bukkit.Bukkit
45
import org.bukkit.command.Command
56
import org.bukkit.command.CommandMap
@@ -151,8 +152,8 @@ object CommandRegistrar {
151152

152153
private fun executeParentCommand(sender: CommandSender, args: Array<out String>): Boolean {
153154
if (args.isEmpty()) {
154-
sender.sendMessage("Usage: /$ROOT_COMMAND <subcommand>")
155-
sender.sendMessage(
155+
sender.sendPrefixed("Usage: /$ROOT_COMMAND <subcommand>")
156+
sender.sendPrefixed(
156157
"Available sub-commands: ${subCommands.keys.sorted().joinToString(", ")}"
157158
)
158159
return true
@@ -161,16 +162,16 @@ object CommandRegistrar {
161162
val subName = args[0].lowercase()
162163
val subCommand = subCommands[subName]
163164
if (subCommand == null) {
164-
sender.sendMessage("Unknown sub-command: ${args[0]}")
165-
sender.sendMessage(
165+
sender.sendPrefixed("Unknown sub-command: ${args[0]}")
166+
sender.sendPrefixed(
166167
"Available sub-commands: ${subCommands.keys.sorted().joinToString(", ")}"
167168
)
168169
return true
169170
}
170171

171172
subCommand.permission?.let { perm ->
172173
if (!sender.hasPermission(perm)) {
173-
sender.sendMessage("You do not have permission to use this command.")
174+
sender.sendPrefixed("You do not have permission to use this command.")
174175
return true
175176
}
176177
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.example.exampleplugin.utils
2+
3+
import net.kyori.adventure.text.Component
4+
import net.kyori.adventure.text.minimessage.MiniMessage
5+
import org.bukkit.command.CommandSender
6+
7+
/**
8+
* Utility for sending prefix-decorated messages to players and command senders.
9+
*
10+
* Call [init] once during plugin startup (and again after a config reload) to
11+
* load the configured prefix. The prefix string may be plain text or a
12+
* MiniMessage-formatted string such as `"<gray>[<gold>ExamplePlugin<gray>]"`.
13+
*
14+
* ### Usage
15+
*
16+
* ```kotlin
17+
* import com.example.exampleplugin.utils.sendPrefixed
18+
*
19+
* // Plain text or MiniMessage string
20+
* player.sendPrefixed("Hello!")
21+
* player.sendPrefixed("<green>Operation successful!")
22+
*
23+
* // Adventure Component
24+
* player.sendPrefixed(Component.text("Hello!", NamedTextColor.GREEN))
25+
* ```
26+
*/
27+
object MessageUtil {
28+
29+
private val mm = MiniMessage.miniMessage()
30+
private var prefixComponent: Component = Component.empty()
31+
32+
/**
33+
* Loads the prefix from [prefixString].
34+
*
35+
* [prefixString] may be a plain text string (e.g. `"[ExamplePlugin]"`) or
36+
* a MiniMessage-formatted string (e.g. `"<gray>[<gold>ExamplePlugin<gray>]"`).
37+
* Call this method during plugin startup and again whenever the configuration
38+
* is reloaded.
39+
*
40+
* @param prefixString the prefix text to display before every message
41+
*/
42+
fun init(prefixString: String) {
43+
prefixComponent = mm.deserialize(prefixString)
44+
}
45+
46+
/**
47+
* Sends [message] to [sender] with the configured prefix prepended.
48+
*
49+
* [message] may be a plain text string or a MiniMessage-formatted string.
50+
*
51+
* @param sender the recipient
52+
* @param message the message string (plain text or MiniMessage-formatted)
53+
*/
54+
fun sendPrefixed(sender: CommandSender, message: String) {
55+
sender.sendMessage(build(mm.deserialize(message)))
56+
}
57+
58+
/**
59+
* Sends [message] to [sender] with the configured prefix prepended.
60+
*
61+
* @param sender the recipient
62+
* @param message the [Component] to send
63+
*/
64+
fun sendPrefixed(sender: CommandSender, message: Component) {
65+
sender.sendMessage(build(message))
66+
}
67+
68+
private fun build(message: Component): Component =
69+
prefixComponent.append(Component.space()).append(message)
70+
}
71+
72+
/**
73+
* Sends [message] to this sender with the plugin prefix prepended.
74+
*
75+
* [message] may be a plain text string or a MiniMessage-formatted string.
76+
*
77+
* @receiver the message recipient
78+
* @param message the message string (plain text or MiniMessage-formatted)
79+
*/
80+
fun CommandSender.sendPrefixed(message: String) = MessageUtil.sendPrefixed(this, message)
81+
82+
/**
83+
* Sends [message] to this sender with the plugin prefix prepended.
84+
*
85+
* @receiver the message recipient
86+
* @param message the [Component] to send
87+
*/
88+
fun CommandSender.sendPrefixed(message: Component) = MessageUtil.sendPrefixed(this, message)

0 commit comments

Comments
 (0)