Dialogs
Dialog Engine
Dialog Engine is a plugin for Minecraft's own dialog screens: the real menus the client has drawn since 1.21.6, with text, items, text boxes, dropdowns, switches, sliders and buttons. You write them in YAML, and the plugin opens them, fills them in for each player and runs what their buttons say.
- Server owners write dialogs in a folder and give them commands, permissions, conditions, lists and actions. No code.
- Developers ship dialogs with their own plugin, add their own actions, placeholders and lists, and hear every click as an event.
It needs no resource pack. A dialog is drawn by the game itself, in the game's own look. (The one exception is drawn dialogs, which are pictures and bring a small pack of their own.)
Installing#
- 1
Get the jar
Download Dialog Engine from SpigotMC.
- 2
Drop it in
The jar into
plugins/, then restart. It runs on Spigot or Paper 1.21.7 or newer, one jar for both, on Java 21. Players need a 1.21.6 or newer client, since that is when dialogs arrived in the game. PlaceholderAPI is optional. - 3
Look at what it made
plugins/DialogEngine/ config.yml AI-GUIDE.md ← the format, for an AI assistant dialogs/ ← yours samples/ ← ten working examples drawn/ samples/ ← two drawn examples
On a server older than 1.21.7 the plugin says so in the console and disables itself.
What talks to the internet
Two things, and both can be turned off in config.yml. update-check asks
SpigotMC for the latest version. telemetry sends an anonymous count every
30 seconds: a random id, the number of players online and the names of
plugins that depend on Dialog Engine. Nothing else.
Drawn dialogs are the third: /de edit, and a drawn
dialog whose dialog.yml you edited by hand, send that one dialog to the
editor to be drawn. Plain YAML dialogs never leave the server.
Your first dialog#
- 1
Make a file
plugins/DialogEngine/dialogs/myserver/hello.yml:hello: command: hello title: "<gold><bold>Hello" body: - "<gray>Welcome, <white>{player}</white>. There are {online} of us online." buttons: - label: "<green>Thanks" actions: - "[message] <green>Have fun!" - "[close]"That is the dialog
myserver:hello. The folder is the namespace and the key is the name. - 2
Reload
/de reloadIt tells you how many dialogs loaded and lists every problem it found, with the file each one is in.
- 3
Open it
/helloOr
/de open myserver:hello, which works on any dialog whether it has a command or not.
Where dialogs live#
Every folder in dialogs/ is a namespace, and every top-level key in a
.yml file inside it is a dialog: welcome in
dialogs/samples/menus.yml is samples:welcome. Subfolders are read too and
stay in the same namespace. Files sitting directly in dialogs/ are in the
namespace server.
Names are lower case: a-z, 0-9, _, . and -. The same name twice in
one namespace is reported, and the later one wins.
When one dialog names another ([open] confirm, if-missing: pick), a name
without a namespace means one in the same namespace.
Text everywhere is MiniMessage
(<gold>, <bold>, <#3670f8>), never & codes. Quote any string that
starts with <, [, {, % or *, or has : in it, or YAML will read it
as something else.
Writing dialogs with an AI
The plugin writes plugins/DialogEngine/AI-GUIDE.md on every start. It is
the format plus the rules that catch the usual mistakes, written for an AI
assistant to read. Hand it over with what you want the screen to do.
The format#
A complete dialog, a shop with a slider:
shop:
command: shop # /shop opens it (optional)
aliases: [store]
permission: myserver.shop # who may open it themselves (optional)
title: "<gold><bold>Shop"
columns: 2 # buttons side by side
escape: true # Escape closes it (default true)
after: wait # wait | close | none, see config.yml
body:
- "<gray>You have <white>%vault_eco_balance%</white> coins." # text
- item: diamond # an item, with words beside it
name: "<aqua>Diamond"
description: "<gray>A coin each"
inputs:
- key: amount
type: slider
label: "How many"
min: 1
max: 64
step: 1
initial: 16
buttons:
- label: "<green>Buy"
tooltip: "Buy {amount} diamonds"
actions:
- "[if] %vault_eco_balance% >= {amount}" # stop unless they can pay
- "[console] eco take {player} {amount}"
- "[console] give {player} diamond {amount}"
- "[message] <green>Bought {amount} diamonds."
- "[close]"
exit: # the button Escape also presses
label: "Close"The dialog itself#
| Key | What it does |
|---|---|
title: | The heading. MiniMessage |
external-title: | A shorter title for where the game names the dialog outside it, such as on a button that opens it |
command: | A command that opens it, one word of a-z, 0-9, _ and -. See Its own command |
aliases: | More names for that command |
permission: | What a player needs to open it themselves, by its command or from another dialog |
args: | Names for the command's arguments, which become placeholders. See A dialog about somebody |
if-missing: | The dialog to open instead when an argument is left out |
defaults: | Values the dialog starts with, as key: value |
type: | notice, confirmation, multi_action (or menu) or links. Leave it out and the buttons decide |
columns: | How many buttons side by side. Default 2 |
escape: | Whether Escape closes it. Default true |
pause: | Whether it pauses a single-player game. Default false, and not allowed with after: none |
after: | What the screen does the moment a button is pressed: wait, close or none. Default from default-after in config |
body: | What is drawn above the inputs. See Body |
inputs: | Text boxes, dropdowns, switches and sliders. See Inputs |
buttons: | See Buttons |
exit: | The button at the bottom, which Escape also presses |
lists: | Lists of rows the dialog can repeat over. See Lists and pages |
json: | Send a file of the game's own dialog JSON instead. See Raw JSON dialogs |
Types#
The layout follows the buttons. One button and no exit: is a notice.
More is a grid of buttons, columns: wide, with the exit: button under it.
Say type: confirmation, or just write confirm: and cancel: buttons in
place of buttons:, for a yes/no screen. type: links is the game's server
links screen, the links your server sends in its configuration.
rules_decline:
title: "Decline the rules?"
type: confirmation
escape: false
body:
- "<gray>You can't play here without accepting them."
confirm:
label: "<red>Leave"
actions: ["[console] kick {player} You need to accept the rules to play."]
cancel:
label: "Read them again"
actions: ["[back]"]Body#
A body entry is a string, which is a line of text, or a map:
| Key | What it does |
|---|---|
text: | MiniMessage. A list is several lines. width: 1 to 1024, default 200 |
item: | An item id, drawn with description: beside it. Also name:, lore:, amount:, model: (an item model id), glow:, tooltip: (false turns off its hover tooltip), decorations: (false hides its count and durability bar), and width:/height: 1 to 256 |
head: | A player's name: the item is their head |
if: | Only drawn when this holds. See Conditions |
for-each: | Drawn once per row of a list. See Lists and pages |
body:
- head: "{target}"
width: 24
height: 24
description: "<gray>Tell us what <white>{target}</white> did."
- text:
- "<white>1.</white> <gray>Be kind."
- "<white>2.</white> <gray>No cheats."
width: 260Inputs#
type: | Keys | What {key} holds |
|---|---|---|
text | max-length, lines and height for a text area, initial | What was typed |
options | options:, a list of {id, text, initial} (or - id: Text), or a for-each | The chosen id. {key.label} is its text |
toggle | initial, on-true, on-false | true or false. {key.text} is on-true or on-false |
slider | min, max, step, initial, format (a translation key) | The number |
Every input needs a key: of letters, digits and _, and takes label:,
label-visible:, width: and if:.
inputs:
- key: reason
type: options
label: "Reason"
options:
- { id: cheating, text: "Cheating", initial: true }
- { id: chat, text: "Chat abuse" }
- key: details
type: text
label: "What happened?"
lines: 3
height: 48
max-length: 200
required: true
min-length: 10
error: "Tell us a little more: at least 10 characters."Checks#
A text input can have required: true, min-length:, pattern: (a Java
regex the whole value must match) and error: (the message to show). When a
button is pressed with an input that fails, the dialog is drawn again with
what was typed still in it and the message on top. The message is also
{error}, which the dialog can print where it likes instead.
A button with validate: false skips the checks, which is what a Cancel
button wants. The exit: button never checks.
What a player typed is escaped wherever it is printed, so nobody can put a colour or a click into somebody else's screen.
Buttons#
| Key | What it does |
|---|---|
label: | Its text |
tooltip: | Shown on hover |
width: | 1 to 1024, default 150 |
permission: | Hidden from a player without it |
if: | Only shown when this holds |
for-each: | One button per row of a list |
validate: | false skips the input checks |
id: | A name for your own plugin to recognise it by |
actions: | What it does. See Actions |
For a button that does one thing there are shorthands, each one action:
command:, console:, open:, url:, copy:, suggest:, message:,
close: true and back: true.
buttons:
- label: "Warps"
open: warps
- label: "Website"
url: "https://example.com"
- label: "Close"
close: trueActions#
A button's actions: run in order, top to bottom. A line without a [tag] is
a command the player runs.
| Action | What it does |
|---|---|
[player] cmd | The player runs it, with their own permissions |
[console] cmd | The console runs it |
[message] text | Tells the player. MiniMessage |
[broadcast] text | Tells everybody |
[actionbar] text | Shows it above the hotbar |
[title] title | subtitle | Shows a title |
[sound] id [volume] [pitch] | Plays a sound: [sound] entity.experience_orb.pickup 1 1.4 |
[open] id [key=value ...] | Opens another dialog, carrying this one's values (and what was typed) over |
[back] | The dialog this one was opened from, as it was |
[refresh] | This one again, with what was typed still in it |
[page] next|prev|first|last|N | Turns the page of a paged list |
[set] key=value ... | Sets a value for the actions after, and any dialog opened |
[clear] [keys] | Forgets what was typed: every input, or the keys named |
[close] | Closes the dialog |
[delay] ticks | Waits before the rest. 20 ticks is a second |
[if] condition | Stops here unless it holds |
[permission] node | Stops here unless the player has it |
[stop] | Stops here |
[url] link | Opens a link |
[copy] text | Copies to the clipboard |
[suggest] /cmd | Puts a command in the chat box, ready to finish |
[myplugin:tag] ... | Whatever another plugin registered |
[url], [copy] and [suggest] are the game's own button actions, so they
do exactly that only when they are a button's only action. Mixed in with
others, [url] sends the link in chat to click, and [copy] and [suggest]
do nothing.
End a button that should leave the screen with [close], or it stays up.
What a player typed, in a command#
A [console] or [player] command takes a typed value (a text input, a
command's args:, values from /de open, {player_display}) only when it
is letters, digits, _, . and -: a name, a number, an id. Anything else
(@a, a space, a quote) and the button runs nothing. A text input is drawn
again with the unsafe-value message on it, anything else gets the message in
chat, and the console says which placeholder it was. The check happens before
the first action runs, so a click never stops halfway with the money taken and
nothing given.
Where more than that is safe, say how the value is used:
| Filter | What it does |
|---|---|
{key|raw} | As typed, on one line. For the last, free-text argument of a plugin's command: staffchat {player}: {details|raw}. Not for vanilla's say, msg or tell, which read @a in it. In a [player] command it is usually fine: the player could type that command themselves |
{key|json} | Escaped for the inside of a quoted JSON string: tellraw @a {"text":"{msg|json}"} |
{key|snbt} | Escaped for the inside of a quoted SNBT string: give {player} paper[custom_name='{name|snbt}'] |
Values from the dialog itself, from plugins and PlaceholderAPI, and from sliders, toggles and dropdowns (only options the dialog drew count) are not checked. A filter works on them too.
When a command fails#
A command that fails stops the rest. When a [console] or [player]
command fails as far as the server can tell, the actions after it do not run.
For [console] the player is told (command-failed) and the console logs it.
The server can only tell when the command does not exist (the plugin behind it
is missing, say) or throws an error. A command that ran and refused, like an
eco take without the money in the account, has succeeded as far as the
server knows, whatever it printed. So check before taking anything, as the
shop above does with [if] %vault_eco_balance% >= {amount}, using
PlaceholderAPI's Vault expansion.
Placeholders#
{name} works anywhere: titles, text, labels, tooltips, actions and
conditions. A name nothing knows is left as written.
- What the dialog was opened with:
/de open shop Steve coins=40, an API call, a command'sargs:,defaults:, or an[open]or[set]action. - Everything typed into its inputs, once a button is pressed.
- The built-ins:
| Placeholder | Value |
|---|---|
{player} | The player's name (also {player_name}) |
{player_uuid} | Their UUID |
{player_display} | Their display name, as plain text |
{player_world} | The world they are in |
{player_x} {player_y} {player_z} | Their block position |
{player_health} | Their health, rounded up |
{player_level} | Their experience level |
{player_gamemode} | survival, creative, adventure or spectator |
{online} | Players online |
{max_players} | The server's player limit |
{dialog} | This dialog's id |
- Whatever a plugin registered, as
{prefix_key}. See For developers.
PlaceholderAPI#
With PlaceholderAPI installed,
every %placeholder% works too, in the same places. A % in what a player
typed is printed as typed and never filled, so nobody can type a placeholder
into a text box to read somebody else's.
Choices#
{mode?on:Enabled|off:Disabled|Unknown} prints the words for the value
mode has, and the last entry when none matches. $value in the words prints
the value itself:
- "<gray>Chat: {chat?true:<green>on|false:<red>off|$value}"Filters#
{name|raw}, {name|json} and {name|snbt} are for putting what a player
typed into a command. See What a player typed, in a command.
Conditions#
if: works on a body entry, an input and a button, and [if] is the same
thing as an action.
{muted} true unless empty, false, 0, no, off or null
!{muted}
{warnings} >= 3 numbers compare as numbers, anything else as words
{rank} == admin && perm:server.staff
({page} > 1 || {mode} != "quiet mode")==,!=,>,<,>=and<=compare. Two numbers compare as numbers, anything else as words, ignoring case.&&,||,!and brackets combine them.perm:nodeis whether the player has that permission.- A placeholder nothing could fill counts as false.
- Put a value with spaces in it in quotes.
A button's if: and permission: are asked again when it is pressed, so a
button that has stopped applying since the dialog was drawn does nothing.
Lists and pages#
for-each: name on a body entry, a button or an input's options: repeats it
once per row of a list. In each one:
{row.field}is a field of the row.as:renamesrow.{row.index},{row.first}and{row.last}say where in the list it is.
per-page: N pages it, and sets {page}, {pages}, {has_next} and
{has_prev}. [page] next turns. limit: N caps how many rows are shown.
Built in:
| List | Fields |
|---|---|
online_players | name, uuid, world, ping |
other_players | The same, without the player looking |
worlds | name, players, environment |
Vanished players stay hidden from whoever cannot see them.
A dialog can declare its own under lists:, and plugins register more.
warps:
title: Warps
lists:
places:
- { id: spawn, name: "Spawn" }
- { id: arena, name: "<red>Arena" }
buttons:
- for-each: places
label: "{row.name}"
actions: ["[player] warp {row.id}", "[close]"]A paged list of players, eight to a page:
players:
title: "Online <dark_gray>({online})"
body:
- "<gray>Page {page} of {pages}"
buttons:
- for-each: online_players
per-page: 8
label: "{row.name}"
tooltip: "{row.world} · {row.ping}ms"
actions: ["[open] player_card target={row.name}"]
- label: "<gray>← Previous"
if: "{has_prev}"
actions: ["[page] prev"]
- label: "<gray>Next →"
if: "{has_next}"
actions: ["[page] next"]
exit:
label: "Close"Opening a dialog#
Its own command#
command: shop makes /shop open it, and aliases: adds more names. These
commands come and go with /de reload, no restart. With a permission:, a
player without it is refused and does not see the command in tab completion.
A command name another plugin already has stays that plugin's: the dialog goes without, and the console says so.
A dialog about somebody#
args: [target] makes /punish Steve open the dialog with {target} set to
Steve. Tab completion offers online players. The last argument takes the
rest of the line, so args: [target, reason] reads /punish Steve being rude
as Steve and being rude.
if-missing: other_dialog opens another dialog when an argument is left out,
typically a list of players to pick from:
report:
command: report
args: [target]
if-missing: report_pick
title: "Report <red>{target}"
# ...
report_pick:
title: "Who are you reporting?"
buttons:
- for-each: other_players
label: "{row.name}"
actions: ["[open] report target={row.name}"]From the console, or for somebody else#
/de open <id> [player] [key=value ...]/de open samples:report Steve target=Alex opens the report on Steve's
screen, about Alex. Staff opening a dialog this way skip its permission:;
dialogengine.open is the permission that counts. The console and command
blocks always name a player.
From another dialog#
[open] id opens another, carrying every value over, and [back] on that one
returns to the first as it was. The target dialog's permission: is still
checked.
Raw JSON dialogs#
For a dialog Minecraft's format can say and this one cannot, json: file.json
(a file beside the YAML) sends a minecraft:dialog object exactly as written,
with {placeholders} filled. Its buttons do whatever the JSON says, and none
of Dialog Engine's actions or checks apply to them.
Drawn dialogs#
A plain dialog is the game's own widgets. A drawn dialog is a picture: a
screen designed in the dialog editor in the browser, in the
game's own look or any other, shown with a small resource pack. Its buttons
still run Dialog Engine's actions, so [console], [open],
[message] and the rest all work, and placeholders are filled when it is
opened and clicked.
/de edit server:shop_drawnThat prints a link to the editor, which needs no account. The link works once
and lasts three hours. Draw the screen, press Send to server, and it is
written into plugins/DialogEngine/drawn/server/shop_drawn/ and loaded. Run
/de edit on it again to change it.
/de edit only draws. A name a YAML dialog already has is edited in its file,
and the command says so. A name without a namespace is in server.
Each drawn dialog's folder holds a dialog.yml, with its command: and
commandPermission: at the top, like a YAML dialog's. You can edit it by hand
and /de reload: a changed file is sent to the editor to be redrawn, which
needs the internet.
The resource pack#
Drawn dialogs are drawn by one small pack, built from all of them. It is sent
beside whatever pack your server already sends, not instead of it, and it
is only built once there is a drawn dialog at all. A server that never uses
/de edit never asks anybody to download anything.
By default the plugin serves the pack itself, on port 8165. Set
pack.public-host to your server's public address or domain, and open that
port, or players outside your machine cannot download it. Or host the pack
yourself: /de pack writes it to a file, and pack.url says where you put
it. See Config.
Players with dialogengine.pack.notify are told the address the pack is
downloaded from when it fails to load for them, which is the quickest way to
spot a missing public-host.
Samples#
On the first start the plugin writes ten sample dialogs into
plugins/DialogEngine/dialogs/samples/, one file each: a welcome notice,
server rules, a shop, kits, warps, a player list, a profile, a report form, a
settings page with every input type, and a menu of them all. Open the menu
with /dialog-samples. Only players with dialogengine.samples (ops) can
open them.
Two more are drawn dialogs, in plugins/DialogEngine/drawn/samples/: a
punishment panel (/sample-punish <player>) and a six-page social settings
menu (/sample-social). /de edit samples:punish opens one in the editor.
Their pack is sent when a player opens one, never on join, so the samples
alone never ask anybody to download anything.
They are yours to copy, change or delete. Deleting one file keeps it deleted;
deleting the whole folder brings them all back on the next start. Set
samples: false in config.yml and they are never written.
Commands#
/dialogengine, with /de and /dialogs as aliases.
| Command | What it does | Permission |
|---|---|---|
/de list | Every dialog, by namespace, with its command | dialogengine.list |
/de open <id> [player] [key=value ...] | Open one, with values | dialogengine.open |
/de info <id> | What a dialog is made of: type, buttons, inputs, command, permission | dialogengine.info |
/de edit <id> | Draw one in the browser editor, or make a new one | dialogengine.edit |
/de pack | Write the drawn dialogs' resource pack to a file | dialogengine.pack |
/de reload | Read everything again, and say what is wrong | dialogengine.reload |
A subcommand you cannot run is not offered in tab completion, and /de on
its own lists only the ones you can.
/dialogclick is what a click on a drawn dialog runs. It does only what the
dialog on that player's screen allows, so it needs no permission, and typing
it yourself does nothing.
Permissions#
One node per subcommand, always dialogengine.<subcommand>, and every one
of them is op-only by default.
| Node | What it allows |
|---|---|
dialogengine.list | /de list, every dialog on the server and its command |
dialogengine.open | /de open <id> on yourself: any dialog, whatever permission: the dialog itself asks for |
dialogengine.open.others | /de open <id> <player> on somebody else, on top of dialogengine.open |
dialogengine.info | /de info <id>, what a dialog is made of |
dialogengine.edit | /de edit <id>, drawing a dialog in the browser editor or making a new one |
dialogengine.pack | /de pack, writing the drawn dialogs' resource pack to a file |
dialogengine.reload | /de reload, reading every dialog again and seeing what is wrong |
dialogengine.pack.notify | Being told the address players download the drawn dialogs' pack from, when it fails to load for you |
dialogengine.samples | Opening the sample dialogs |
dialogengine.update | Being told on joining that a newer Dialog Engine is on SpigotMC |
dialogengine.admin | All of the above, in one node |
dialogengine.admin is a parent for a server that does not want to think
about it. Nothing checks it directly.
dialogengine.edit is the one worth a second look before granting it widely:
pressing Send in the editor writes files into your server's dialog folder.
A dialog's own permission: (who may open it) and a button's (who sees it)
are whatever node you write there. Dialog Engine declares none of them, so
give them to players with your permissions plugin as usual:
/lp group default permission set myserver.shop true
/lp group admin permission set dialogengine.admin trueConfig#
plugins/DialogEngine/config.yml. /de reload reads it again.
| Key | Default | What it does |
|---|---|---|
samples | true | Write the sample dialogs when their folder is missing |
update-check | true | Say in the console and on join when an update is out |
telemetry | true | The anonymous usage count described under Installing |
click-lifetime-minutes | 30 | How long a dialog's buttons keep working after it is drawn. Drawing it again starts the clock over |
default-after | wait | What a dialog does the moment a button is pressed, when it does not say with after:. See below |
close-delay-ticks | 10 | Under wait, how long to hold the waiting screen for a plugin to answer before closing it |
click-sound | ui.button.click | The sound a click on a drawn dialog makes. Blank for none |
validation-format | <red>⚠ {error} | How a failed input check is shown, when the dialog does not print {error} itself |
messages.* | Everything the plugin says to players. MiniMessage | |
edit.url | https://studio.resourcepack.ai | Where the dialog editor is. Must be https:// (plain http:// only for one on the same machine) |
pack.send | true | Send the drawn dialogs' pack to players as they join |
pack.required | false | Kick players who decline it |
pack.port | 8165 | The port the built-in pack server listens on |
pack.bind | blank | The address it listens on. Blank is every address the machine has |
pack.public-host | blank | The address players download it from. Set this to your server's public address or domain |
pack.url | blank | Host the pack yourself and put its URL here; the built-in server is then off |
pack.prompt | The line shown when the pack is offered |
The three after behaviours:
wait: the screen stays up with "waiting for the server" until the plugin answers, so one dialog replaces another with no flash of the world in between. Recommended.close: it closes at once, then the next dialog (if any) opens.none: it stays exactly as it is.
How a click is kept safe#
Every button is a server callback bound to the drawing of the dialog the player saw: the values it was drawn with, and its inputs. Nothing a player can type in chat presses a button.
- Only the latest drawing can be pressed. A new one (another dialog, a refresh, a failed check) retires the buttons of the one before.
- A button's
permission:andif:are asked again when it is pressed. - Buttons stop working
click-lifetime-minutesafter the dialog was drawn, and a player gets at most ten clicks a second. - A drawn dialog's clicks work once per showing, only while that dialog is on
the player's screen, and only run a command that is in its
dialog.yml.
For developers#
Add depend: [DialogEngine] (or softdepend, and check
DialogEngine.isEnabled() first) to your plugin.yml, and compile against
the jar. The API is in ai.resourcepack.dialogs.api, and every call is
main-thread only, like the rest of Bukkit.
DialogEngineAPI dialogs = DialogEngine.api();
// Ship your dialogs: copy defaults into your data folder, then hand it over.
// Server owners edit them there; /de reload reads them again.
saveResource("dialogs/menu.yml", false);
dialogs.registerFolder(this, "myplugin", new File(getDataFolder(), "dialogs"));
// Open one, with values for its {placeholders}.
dialogs.open(player, "myplugin:menu", Map.of("coins", "120"));
dialogs.refresh(player); // the player's current dialog, drawn again
dialogs.close(player);
// A new action: [myplugin:give] diamond 3
dialogs.registerAction(this, "myplugin:give", (ctx, arg) -> {
String[] p = arg.split(" ");
ctx.viewer().getInventory().addItem(new ItemStack(Material.matchMaterial(p[0]), Integer.parseInt(p[1])));
ctx.refresh();
});
// {myplugin_coins}
dialogs.registerPlaceholders(this, "myplugin_", (viewer, ctx, key) ->
key.equals("coins") ? String.valueOf(economy.balance(viewer)) : null);
// for-each: myplugin_quests
dialogs.registerList(this, "myplugin_quests", (viewer, ctx) -> quests.of(viewer).stream()
.map(q -> Map.of("id", q.id(), "name", dialogs.escape(q.name()), "done", String.valueOf(q.done())))
.toList());A dialog built in code is a ConfigurationSection in the same format:
registerDialog(plugin, "myplugin:x", section).
Values you pass are read as MiniMessage. Anything a player wrote goes
through dialogs.escape(...) first. An action's argument arrives with its
placeholders filled and what a player typed in it as typed, so check it
before it goes into a command.
Everything a plugin registers is dropped when it disables.
Beyond those, the API has current(player) and values(player) for the
dialog a player has open, ids() and info(id) for what is loaded,
canOpen(player, id) for a dialog's permission:, textJson(...) to render
MiniMessage the way a dialog does, and reload().
Events#
| Event | When |
|---|---|
DialogOpenEvent | A dialog is about to be drawn: opened, refreshed, paged or returned to. Cancellable; values() can be changed, and isReopened() says whether it is the same dialog again |
DialogClickEvent | A button was pressed, its inputs passed, its actions not yet run. Cancellable; getButtonId(), getInputs(), getActions() (changeable), and getContext() to open, refresh or close |
DialogInputErrorEvent | An input failed its check; the message can be changed |
DialogEngineReloadEvent | Everything was read again |
A button with an id: and no actions is a button your plugin answers in
DialogClickEvent:
@EventHandler
public void onClick(DialogClickEvent event) {
if (!event.getDialogId().equals("myplugin:menu")) return;
if (event.getButtonId().equals("claim")) {
rewards.claim(event.getPlayer());
event.getContext().close();
}
}Support#
Questions, bugs and ideas: the ResourcePack AI Discord.