Skip to content

Commit 62def25

Browse files
authored
Merge pull request #15 from Trilleo/copilot/add-utility-guide-to-docs
Add UTILITY_GUIDE and remove duplicate content from DEVELOPER_GUIDE
2 parents eaadfd4 + ee4f3e1 commit 62def25

3 files changed

Lines changed: 216 additions & 70 deletions

File tree

‎README.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,6 @@ src/main/kotlin/com/example/exampleplugin/
4040
See [`docs/DEVELOPER_GUIDE.md`](docs/DEVELOPER_GUIDE.md) for detailed instructions on creating commands, listeners,
4141
GUIs, working with the configuration system, and managing player and server data.
4242

43+
See [`docs/UTILITY_GUIDE.md`](docs/UTILITY_GUIDE.md) for documentation on the built-in utility helpers such as
44+
the `itemStack` DSL builder and `CountdownHelper`.
45+

‎docs/DEVELOPER_GUIDE.md‎

Lines changed: 4 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,77 +1096,11 @@ player.sendPlayerListHeaderAndFooter(Component.empty(), Component.empty())
10961096

10971097
---
10981098

1099-
## ItemStack Builder DSL
1099+
## Utilities
11001100

1101-
Building `ItemStack` instances with custom names, lore, enchantments, and flags normally requires verbose
1102-
boilerplate. The `itemStack` DSL in `com.example.exampleplugin.utils` lets you create fully configured items in a
1103-
single expression. All text is parsed through
1104-
[MiniMessage](https://docs.advntr.dev/minimessage/index.html), so rich formatting tags like `<bold>`, `<red>`,
1105-
and `<gradient>` work out of the box.
1106-
1107-
### Before (vanilla API)
1108-
1109-
```kotlin
1110-
val item = ItemStack(Material.DIAMOND_SWORD)
1111-
val meta = item.itemMeta
1112-
meta.displayName(MiniMessage.miniMessage().deserialize("<bold><gradient:gold:yellow>Excalibur</gradient></bold>"))
1113-
meta.lore(
1114-
listOf(
1115-
MiniMessage.miniMessage().deserialize("<gray>A legendary blade"),
1116-
MiniMessage.miniMessage().deserialize("<gray>Damage: <red>+20")
1117-
)
1118-
)
1119-
meta.addEnchant(Enchantment.SHARPNESS, 5, true)
1120-
meta.isUnbreakable = true
1121-
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS)
1122-
item.itemMeta = meta
1123-
```
1124-
1125-
### After (using the DSL)
1126-
1127-
```kotlin
1128-
import com.example.exampleplugin.utils.itemStack
1129-
1130-
val item = itemStack(Material.DIAMOND_SWORD) {
1131-
name("<bold><gradient:gold:yellow>Excalibur</gradient></bold>")
1132-
lore("<gray>A legendary blade", "<gray>Damage: <red>+20")
1133-
enchant(Enchantment.SHARPNESS, 5)
1134-
unbreakable(true)
1135-
flag(ItemFlag.HIDE_ENCHANTS)
1136-
}
1137-
```
1138-
1139-
### Builder Methods
1140-
1141-
| Method | Signature | Description |
1142-
|:------------------|:----------------------------|:------------------------------------------------|
1143-
| `name` | `name(String)` | Set the display name (MiniMessage) |
1144-
| `lore` | `lore(vararg String)` | Set lore lines (each parsed with MiniMessage) |
1145-
| `enchant` | `enchant(Enchantment, Int)` | Add an enchantment at the given level |
1146-
| `unbreakable` | `unbreakable(Boolean)` | Make the item unbreakable |
1147-
| `amount` | `amount(Int)` | Set the stack size |
1148-
| `flag` | `flag(vararg ItemFlag)` | Add one or more item flags |
1149-
| `customModelData` | `customModelData(Int)` | Set the custom model data value |
1150-
| `meta` | `meta(ItemMeta.() -> Unit)` | Escape hatch for direct `ItemMeta` manipulation |
1151-
1152-
### Escape Hatch Example
1153-
1154-
For advanced use-cases not covered by the builder methods, the `meta` block gives you direct access to the
1155-
`ItemMeta`. Any changes made inside `meta` are applied **after** all other builder properties, so they take
1156-
precedence:
1157-
1158-
```kotlin
1159-
import com.example.exampleplugin.utils.itemStack
1160-
1161-
val head = itemStack(Material.PLAYER_HEAD) {
1162-
name("<yellow>Custom Head")
1163-
meta {
1164-
// 'this' is the ItemMeta — cast and use any Paper API method
1165-
(this as org.bukkit.inventory.meta.SkullMeta)
1166-
.owningPlayer = org.bukkit.Bukkit.getOfflinePlayer("Notch")
1167-
}
1168-
}
1169-
```
1101+
The `utils` package (`com.example.exampleplugin.utils`) contains helper classes and functions that reduce
1102+
boilerplate across the plugin. See the [Utility Guide](UTILITY_GUIDE.md) for full documentation, including the
1103+
`itemStack` DSL builder, `CountdownHelper`, and `DisplayLocation`.
11701104

11711105
---
11721106

‎docs/UTILITY_GUIDE.md‎

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# ExamplePlugin - Utility Guide
2+
3+
This guide covers the utility helpers provided in `com.example.exampleplugin.utils`. Each utility is designed to
4+
reduce boilerplate and provide commonly needed functionality out of the box.
5+
6+
| Utility | Description |
7+
|:---------------------|:---------------------------------------------------------------------|
8+
| `itemStack` | DSL builder for creating `ItemStack` instances concisely |
9+
| `CountdownHelper` | Per-player countdown with configurable display and sound |
10+
| `DisplayLocation` | Enum that controls where countdown messages are rendered |
11+
12+
---
13+
14+
## ItemStack Builder DSL
15+
16+
Building `ItemStack` instances with custom names, lore, enchantments, and flags normally requires verbose
17+
boilerplate. The `itemStack` DSL in `com.example.exampleplugin.utils` lets you create fully configured items in a
18+
single expression. All text is parsed through
19+
[MiniMessage](https://docs.advntr.dev/minimessage/index.html), so rich formatting tags like `<bold>`, `<red>`,
20+
and `<gradient>` work out of the box.
21+
22+
### Before (vanilla API)
23+
24+
```kotlin
25+
val item = ItemStack(Material.DIAMOND_SWORD)
26+
val meta = item.itemMeta
27+
meta.displayName(MiniMessage.miniMessage().deserialize("<bold><gradient:gold:yellow>Excalibur</gradient></bold>"))
28+
meta.lore(
29+
listOf(
30+
MiniMessage.miniMessage().deserialize("<gray>A legendary blade"),
31+
MiniMessage.miniMessage().deserialize("<gray>Damage: <red>+20")
32+
)
33+
)
34+
meta.addEnchant(Enchantment.SHARPNESS, 5, true)
35+
meta.isUnbreakable = true
36+
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS)
37+
item.itemMeta = meta
38+
```
39+
40+
### After (using the DSL)
41+
42+
```kotlin
43+
import com.example.exampleplugin.utils.itemStack
44+
45+
val item = itemStack(Material.DIAMOND_SWORD) {
46+
name("<bold><gradient:gold:yellow>Excalibur</gradient></bold>")
47+
lore("<gray>A legendary blade", "<gray>Damage: <red>+20")
48+
enchant(Enchantment.SHARPNESS, 5)
49+
unbreakable(true)
50+
flag(ItemFlag.HIDE_ENCHANTS)
51+
}
52+
```
53+
54+
### Builder Methods
55+
56+
| Method | Signature | Description |
57+
|:------------------|:----------------------------|:------------------------------------------------|
58+
| `name` | `name(String)` | Set the display name (MiniMessage) |
59+
| `lore` | `lore(vararg String)` | Set lore lines (each parsed with MiniMessage) |
60+
| `enchant` | `enchant(Enchantment, Int)` | Add an enchantment at the given level |
61+
| `unbreakable` | `unbreakable(Boolean)` | Make the item unbreakable |
62+
| `amount` | `amount(Int)` | Set the stack size |
63+
| `flag` | `flag(vararg ItemFlag)` | Add one or more item flags |
64+
| `customModelData` | `customModelData(Int)` | Set the custom model data value |
65+
| `meta` | `meta(ItemMeta.() -> Unit)` | Escape hatch for direct `ItemMeta` manipulation |
66+
67+
### Escape Hatch Example
68+
69+
For advanced use-cases not covered by the builder methods, the `meta` block gives you direct access to the
70+
`ItemMeta`. Any changes made inside `meta` are applied **after** all other builder properties, so they take
71+
precedence:
72+
73+
```kotlin
74+
import com.example.exampleplugin.utils.itemStack
75+
76+
val head = itemStack(Material.PLAYER_HEAD) {
77+
name("<yellow>Custom Head")
78+
meta {
79+
// 'this' is the ItemMeta — cast and use any Paper API method
80+
(this as org.bukkit.inventory.meta.SkullMeta)
81+
.owningPlayer = org.bukkit.Bukkit.getOfflinePlayer("Notch")
82+
}
83+
}
84+
```
85+
86+
---
87+
88+
## CountdownHelper
89+
90+
`CountdownHelper` runs a per-player countdown and displays the progress through a configurable
91+
[`DisplayLocation`](#displaylocation). It schedules a repeating sync task that ticks every second, shows an
92+
optional message on each tick, and fires an optional finish message and callback when the countdown reaches zero.
93+
94+
### Usage
95+
96+
```kotlin
97+
import com.example.exampleplugin.utils.CountdownHelper
98+
import com.example.exampleplugin.utils.DisplayLocation
99+
import net.kyori.adventure.key.Key
100+
import net.kyori.adventure.sound.Sound
101+
102+
CountdownHelper().start(
103+
plugin = plugin,
104+
player = player,
105+
seconds = 10,
106+
displayLocation = DisplayLocation.ACTION_BAR,
107+
message = "<yellow>Starting in <bold>{seconds}</bold> (<gray>{time}</gray>)",
108+
finishMessage = "<green>Go!",
109+
sound = Sound.sound(Key.key("minecraft:ui.button.click"), Sound.Source.MASTER, 1f, 1f),
110+
finishSound = Sound.sound(Key.key("minecraft:entity.player.levelup"), Sound.Source.MASTER, 1f, 1f),
111+
onFinish = { p -> p.sendMessage("Started!") }
112+
)
113+
```
114+
115+
### Parameters
116+
117+
| Parameter | Type | Required | Default | Description |
118+
|:------------------|:--------------------|:---------|:---------------------|:---------------------------------------------------------------------|
119+
| `plugin` | `JavaPlugin` | Yes | — | The owning plugin, used to schedule the internal task |
120+
| `player` | `Player` | Yes | — | The player to target |
121+
| `seconds` | `Int` | Yes | — | Total number of seconds to count down from (must be > 0) |
122+
| `displayLocation` | `DisplayLocation` | Yes | — | Where messages are shown; use `DisplayLocation.NONE` to suppress all |
123+
| `message` | `String?` | No | `null` | MiniMessage string shown on every tick; omit to skip per-tick output |
124+
| `finishMessage` | `String?` | No | `null` | MiniMessage string shown when the countdown ends; omit to skip |
125+
| `bossBarColor` | `BossBar.Color` | No | `BossBar.Color.BLUE` | Boss bar colour; only used when `displayLocation` is `BOSS_BAR` |
126+
| `sound` | `Sound?` | No | `null` | Sound played on every tick; pass `null` for silence |
127+
| `finishSound` | `Sound?` | No | `null` | Sound played when the countdown ends; pass `null` for silence |
128+
| `onFinish` | `(Player) -> Unit` | Yes | — | Callback invoked with the player when the countdown reaches zero |
129+
130+
### Message Placeholders
131+
132+
Both `message` and `finishMessage` support the following placeholders:
133+
134+
| Placeholder | Output example |
135+
|:-------------|:--------------------|
136+
| `{seconds}` | `5s` |
137+
| `{time}` | `1h 2m 3s` |
138+
139+
Either or both placeholders may be omitted from the message string.
140+
141+
### Example (Chat Countdown)
142+
143+
```kotlin
144+
import com.example.exampleplugin.utils.CountdownHelper
145+
import com.example.exampleplugin.utils.DisplayLocation
146+
147+
CountdownHelper().start(
148+
plugin = plugin,
149+
player = player,
150+
seconds = 5,
151+
displayLocation = DisplayLocation.CHAT,
152+
message = "<gray>Game starts in <yellow>{seconds}",
153+
finishMessage = "<green><bold>Game started!",
154+
onFinish = { p -> p.sendMessage("Good luck!") }
155+
)
156+
```
157+
158+
### Example (Boss Bar Countdown)
159+
160+
```kotlin
161+
import com.example.exampleplugin.utils.CountdownHelper
162+
import com.example.exampleplugin.utils.DisplayLocation
163+
import net.kyori.adventure.bossbar.BossBar
164+
165+
CountdownHelper().start(
166+
plugin = plugin,
167+
player = player,
168+
seconds = 30,
169+
displayLocation = DisplayLocation.BOSS_BAR,
170+
bossBarColor = BossBar.Color.RED,
171+
message = "<red>Time remaining: {time}",
172+
finishMessage = "<green>Time's up!",
173+
onFinish = { p -> p.sendMessage("Round over!") }
174+
)
175+
```
176+
177+
---
178+
179+
## DisplayLocation
180+
181+
`DisplayLocation` is an enum used by [`CountdownHelper`](#countdownhelper) to control where countdown messages
182+
are rendered for the player.
183+
184+
### Values
185+
186+
| Value | Behaviour |
187+
|:-------------|:-------------------------------------------------------|
188+
| `NONE` | No message is displayed |
189+
| `CHAT` | Message is sent to the player's chat |
190+
| `TITLE` | Message is shown as a screen title |
191+
| `BOSS_BAR` | Message is shown in a boss bar that depletes over time |
192+
| `ACTION_BAR` | Message is shown above the hotbar |
193+
194+
### Usage
195+
196+
```kotlin
197+
import com.example.exampleplugin.utils.DisplayLocation
198+
199+
// Use with CountdownHelper
200+
CountdownHelper().start(
201+
plugin = plugin,
202+
player = player,
203+
seconds = 10,
204+
displayLocation = DisplayLocation.ACTION_BAR,
205+
onFinish = { /* ... */ }
206+
)
207+
```
208+
209+
---

0 commit comments

Comments
 (0)