|
| 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