Dialog API (#12671)

This commit is contained in:
Jake Potrebic
2025-07-06 11:49:43 -07:00
committed by GitHub
parent f7d5a0a017
commit b4466ec981
116 changed files with 4404 additions and 465 deletions
@@ -0,0 +1,61 @@
package io.papermc.paper.registry.keys;
import static net.kyori.adventure.key.Key.key;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.generated.GeneratedFrom;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import net.kyori.adventure.key.Key;
import org.jspecify.annotations.NullMarked;
/**
* Vanilla keys for {@link RegistryKey#DIALOG}.
*
* @apiNote The fields provided here are a direct representation of
* what is available from the vanilla game source. They may be
* changed (including removals) on any Minecraft version
* bump, so cross-version compatibility is not provided on the
* same level as it is on most of the other API.
*/
@SuppressWarnings({
"unused",
"SpellCheckingInspection"
})
@NullMarked
@GeneratedFrom("1.21.7-rc2")
public final class DialogKeys {
/**
* {@code minecraft:custom_options}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Dialog> CUSTOM_OPTIONS = create(key("custom_options"));
/**
* {@code minecraft:quick_actions}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Dialog> QUICK_ACTIONS = create(key("quick_actions"));
/**
* {@code minecraft:server_links}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Dialog> SERVER_LINKS = create(key("server_links"));
private DialogKeys() {
}
/**
* Creates a typed key for {@link Dialog} in the registry {@code minecraft:dialog}.
*
* @param key the value's key in the registry
* @return a new typed key
*/
public static TypedKey<Dialog> create(final Key key) {
return TypedKey.create(RegistryKey.DIALOG, key);
}
}
@@ -0,0 +1,57 @@
package io.papermc.paper.registry.keys.tags;
import static net.kyori.adventure.key.Key.key;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.generated.GeneratedFrom;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.tag.TagKey;
import net.kyori.adventure.key.Key;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Vanilla tag keys for {@link RegistryKey#DIALOG}.
*
* @apiNote The fields provided here are a direct representation of
* what is available from the vanilla game source. They may be
* changed (including removals) on any Minecraft version
* bump, so cross-version compatibility is not provided on the
* same level as it is on most of the other API.
*/
@SuppressWarnings({
"unused",
"SpellCheckingInspection"
})
@NullMarked
@GeneratedFrom("1.21.7-rc2")
@ApiStatus.Experimental
public final class DialogTagKeys {
/**
* {@code #minecraft:pause_screen_additions}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TagKey<Dialog> PAUSE_SCREEN_ADDITIONS = create(key("pause_screen_additions"));
/**
* {@code #minecraft:quick_actions}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TagKey<Dialog> QUICK_ACTIONS = create(key("quick_actions"));
private DialogTagKeys() {
}
/**
* Creates a tag key for {@link Dialog} in the registry {@code minecraft:dialog}.
*
* @param key the tag key's key
* @return a new tag key
*/
@ApiStatus.Experimental
public static TagKey<Dialog> create(final Key key) {
return TagKey.create(RegistryKey.DIALOG, key);
}
}
@@ -0,0 +1,47 @@
package io.papermc.paper.dialog;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.InlinedRegistryBuilderProvider;
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
import java.util.function.Consumer;
import net.kyori.adventure.dialog.DialogLike;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Keyed;
import org.bukkit.Registry;
import org.jetbrains.annotations.ApiStatus;
/**
* Represents a dialog. Can be created during normal server operation via {@link #create(Consumer)}.
* Can also be created during bootstrap via {@link io.papermc.paper.registry.event.RegistryEvents#DIALOG}.
*/
@ApiStatus.NonExtendable
public interface Dialog extends Keyed, DialogLike {
/**
* Creates a new dialog using the provided builder.
*
* @param value the builder to use for creating the dialog
* @return a new dialog instance
*/
@ApiStatus.Experimental
static Dialog create(final Consumer<RegistryBuilderFactory<Dialog, ? extends DialogRegistryEntry.Builder>> value) {
return InlinedRegistryBuilderProvider.instance().createDialog(value);
}
// Start generate - Dialog
// @GeneratedFrom 1.21.7-rc2
Dialog CUSTOM_OPTIONS = getDialog("custom_options");
Dialog QUICK_ACTIONS = getDialog("quick_actions");
Dialog SERVER_LINKS = getDialog("server_links");
// End generate - Dialog
private static Dialog getDialog(@KeyPattern.Value final String value) {
final Registry<Dialog> registry = RegistryAccess.registryAccess().getRegistry(RegistryKey.DIALOG);
return registry.getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, value));
}
}
@@ -0,0 +1,52 @@
package io.papermc.paper.dialog;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.Nullable;
/**
* A view for a possible response to a dialog.
* There are no guarantees that this is an actual response to a
* dialog form. It is on the plugin to validate that the response
* is valid.
*/
@ApiStatus.Experimental
@ApiStatus.NonExtendable
public interface DialogResponseView {
/**
* Gets the raw payload of the response.
*
* @return the raw payload
*/
@Contract(pure = true)
BinaryTagHolder payload();
/**
* Gets a text value at a key.
*
* @param key the key
* @return the value (or null if it doesn't exist)
*/
@Contract(pure = true)
@Nullable String getText(String key);
/**
* Gets a boolean value at a key.
*
* @param key the key
* @return the value (or null if it doesn't exist)
*/
@Contract(pure = true)
@Nullable Boolean getBoolean(String key);
/**
* Gets a float value at a key.
*
* @param key the key
* @return the value (or null if it doesn't exist)
*/
@Contract(pure = true)
@Nullable Float getFloat(String key);
}
@@ -0,0 +1,7 @@
/**
* This package contains classes and interfaces related to the dialog system in Paper.
*/
@NullMarked
package io.papermc.paper.dialog;
import org.jspecify.annotations.NullMarked;
@@ -0,0 +1,79 @@
package io.papermc.paper.event.player;
import io.papermc.paper.connection.PlayerCommonConnection;
import io.papermc.paper.dialog.DialogResponseView;
import io.papermc.paper.registry.data.dialog.action.DialogActionCallback;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.event.ClickCallback;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* This event is fired for any custom click events.
* @see net.kyori.adventure.text.event.ClickEvent#custom(Key, BinaryTagHolder)
* @see io.papermc.paper.registry.data.dialog.action.DialogAction#customClick(DialogActionCallback, ClickCallback.Options)
*/
@ApiStatus.Experimental
@ApiStatus.NonExtendable
@NullMarked
public abstract class PlayerCustomClickEvent extends Event {
private final Key identifier;
private final PlayerCommonConnection commonConnection;
@ApiStatus.Internal
protected PlayerCustomClickEvent(final Key identifier, final PlayerCommonConnection commonConnection) {
this.identifier = identifier;
this.commonConnection = commonConnection;
}
/**
* The identifier of the custom click event.
*
* @return the identifier
*/
public final Key getIdentifier() {
return this.identifier;
}
/**
* The tag payload of the custom click event.
*
* @return the tag (if any)
*/
public abstract @Nullable BinaryTagHolder getTag();
/**
* The dialog response view of the custom click event.
*
* @return the dialog response view
*/
public abstract @Nullable DialogResponseView getDialogResponseView();
/**
* The common connection of the player.
*
* @return the common connection
*/
public final PlayerCommonConnection getCommonConnection() {
return this.commonConnection;
}
@Override
public HandlerList getHandlers() {
// this will be how handler lists will work on interfaces
return PlayerCustomClickEvent.getHandlerList();
}
public static HandlerList getHandlerList() {
final class Holder {
private static final HandlerList HANDLER_LIST = new HandlerList();
}
return Holder.HANDLER_LIST;
}
}
@@ -1,6 +1,7 @@
package io.papermc.paper.registry;
import io.papermc.paper.datacomponent.DataComponentType;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.tag.TagKey;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
@@ -215,7 +216,11 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
* @see io.papermc.paper.registry.keys.PigVariantKeys
*/
RegistryKey<Pig.Variant> PIG_VARIANT = create("pig_variant");
/**
* Data-driven registry for dialogs.
* @see io.papermc.paper.registry.keys.DialogKeys
*/
RegistryKey<Dialog> DIALOG = create("dialog");
/* ******************* *
@@ -1,6 +1,8 @@
package io.papermc.paper.registry.data;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.function.Consumer;
@@ -12,11 +14,13 @@ import org.jetbrains.annotations.ApiStatus;
public interface InlinedRegistryBuilderProvider {
static InlinedRegistryBuilderProvider instance() {
class Holder {
final class Holder {
static final Optional<InlinedRegistryBuilderProvider> INSTANCE = ServiceLoader.load(InlinedRegistryBuilderProvider.class).findFirst();
}
return Holder.INSTANCE.orElseThrow();
}
MusicInstrument createInstrument(Consumer<RegistryBuilderFactory<MusicInstrument, ? extends InstrumentRegistryEntry.Builder>> value);
Dialog createDialog(Consumer<RegistryBuilderFactory<Dialog, ? extends DialogRegistryEntry.Builder>> value);
}
@@ -0,0 +1,115 @@
package io.papermc.paper.registry.data.dialog;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Range;
import org.jspecify.annotations.Nullable;
/**
* Represents an action button in a dialog, which can be used to trigger actions or navigate within the dialog.
* Action buttons can have labels, tooltips, and associated actions.
*/
@ApiStatus.NonExtendable
public interface ActionButton {
/**
* Creates a new action button with the specified label, tooltip, width, and action.
*
* @param label the label of the button
* @param tooltip the tooltip to display when hovering over the button, or null if no tooltip is needed
* @param width the width of the button
* @param action the action to perform when the button is clicked, or null if no action is associated
* @return a new ActionButton instance
*/
@Contract(value = "_, _, _, _ -> new", pure = true)
static ActionButton create(final Component label, final @Nullable Component tooltip, final int width, final @Nullable DialogAction action) {
return builder(label).tooltip(tooltip).width(width).action(action).build();
}
/**
* Creates a new action button builder with the specified label.
*
* @param label the label of the button
* @return a new ActionButton.Builder instance
*/
@Contract(pure = true, value = "_ -> new")
static ActionButton.Builder builder(final Component label) {
return DialogInstancesProvider.instance().actionButtonBuilder(label);
}
/**
* Returns the label of the action button.
*
* @return the label of the button
*/
@Contract(pure = true)
Component label();
/**
* Returns the tooltip of the action button, or null if no tooltip is set.
*
* @return the tooltip of the button, or null
*/
@Contract(pure = true)
@Nullable Component tooltip();
/**
* Returns the width of the action button.
*
* @return the width of the button
*/
@Contract(pure = true)
@Range(from = 1, to = 1024) int width();
/**
* Returns the action associated with this button, or null if no action is associated.
*
* @return the action to perform when the button is clicked, or null
*/
@Contract(pure = true)
@Nullable DialogAction action();
/**
* A builder for creating ActionButton instances.
*/
@ApiStatus.NonExtendable
interface Builder {
/**
* Sets the tooltip of the action button, or null if no tooltip is desired.
*
* @param tooltip the tooltip of the button, or null
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder tooltip(@Nullable Component tooltip);
/**
* Sets the width of the action button.
*
* @param width the width of the button
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder width(@Range(from = 1, to = 1024) int width);
/**
* Sets the action associated with this button, or null if no action is desired.
*
* @param action the action to perform when the button is clicked, or null
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder action(@Nullable DialogAction action);
/**
* Builds the ActionButton instance with the configured values.
*
* @return a new ActionButton instance
*/
@Contract(value = "-> new", pure = true)
ActionButton build();
}
}
@@ -0,0 +1,211 @@
package io.papermc.paper.registry.data.dialog;
import io.papermc.paper.registry.data.dialog.body.DialogBody;
import io.papermc.paper.registry.data.dialog.input.DialogInput;
import java.util.List;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.util.Index;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.Nullable;
/**
* Represents the base of all dialogs.
*/
@ApiStatus.NonExtendable
public interface DialogBase {
/**
* Creates a new dialog base.
*
* @param title the title of the dialog
* @param externalTitle the external title of the dialog, or null if not set
* @param canCloseWithEscape if the dialog can be closed with the "escape" keybind
* @param pause if the dialog should pause the game when opened (single-player only)
* @param afterAction the action to take after the dialog is closed
* @param body the body of the dialog
* @param inputs the inputs of the dialog
* @return a new dialog base instance
*/
@Contract(value = "_, _, _, _, _, _, _ -> new", pure = true)
static DialogBase create(
final Component title,
final @Nullable Component externalTitle,
final boolean canCloseWithEscape,
final boolean pause,
final DialogAfterAction afterAction,
final List<? extends DialogBody> body,
final List<? extends DialogInput> inputs
) {
return builder(title).externalTitle(externalTitle).canCloseWithEscape(canCloseWithEscape).pause(pause).afterAction(afterAction).body(body).inputs(inputs).build();
}
/**
* Creates a new dialog base builder.
*
* @param title the title of the dialog
* @return a new dialog base builder
*/
@Contract(value = "_ -> new", pure = true)
static Builder builder(final Component title) {
return DialogInstancesProvider.instance().dialogBaseBuilder(title);
}
/**
* The title of the dialog.
*
* @return the title
*/
@Contract(pure = true)
Component title();
/**
* The external title of the dialog. This title
* is used on buttons that open this dialog.
*
* @return the external title or null
*/
@Contract(pure = true)
@Nullable Component externalTitle();
/**
* Returns if this dialog can be closed with the "escape" keybind.
*
* @return if the dialog can be closed with "escape"
*/
@Contract(pure = true)
boolean canCloseWithEscape();
/**
* Returns if this dialog should pause the game when opened (single-player only).
*
* @return if the dialog pauses the game
*/
@Contract(pure = true)
boolean pause();
/**
* The action to take after the dialog is closed.
*
* @return the action to take after the dialog is closed
*/
@Contract(pure = true)
DialogAfterAction afterAction();
/**
* The body of the dialog.
* <p>
* The body is a list of {@link DialogBody} elements that will be displayed in the dialog.
*
* @return the body of the dialog
*/
@Contract(pure = true)
@Unmodifiable List<DialogBody> body();
/**
* The inputs of the dialog.
* <p>
* The inputs are a list of {@link DialogInput} elements that will be displayed in the dialog.
*
* @return the inputs of the dialog
*/
@Contract(pure = true)
@Unmodifiable List<DialogInput> inputs();
/**
* Actions to take after the dialog is closed.
*/
enum DialogAfterAction {
/**
* Closes the dialog and returns to the previous non-dialog screen (if any).
*/
CLOSE("close"),
/**
* Does nothing (keeps the current screen open).
*/
NONE("none"),
/**
* Replaces dialog with a "waiting for response" screen.
*/
WAIT_FOR_RESPONSE("wait_for_response");
public static final Index<String, DialogAfterAction> NAMES = Index.create(DialogAfterAction.class, e -> e.name);
private final String name;
DialogAfterAction(final String name) {
this.name = name;
}
}
/**
* Builder interface for creating dialog bases.
*/
@ApiStatus.NonExtendable
interface Builder {
/**
* Sets the external title of the dialog.
* This title is used on buttons that open this dialog.
*
* @param externalTitle the external title of the dialog, or null if not set
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder externalTitle(@Nullable Component externalTitle);
/**
* Sets whether the dialog can be closed with the "escape" keybind.
*
* @param canCloseWithEscape if the dialog can be closed with "escape"
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder canCloseWithEscape(boolean canCloseWithEscape);
/**
* Sets whether the dialog should pause the game when opened (single-player only).
*
* @param pause if the dialog should pause the game
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder pause(boolean pause);
/**
* Sets the action to take after the dialog is closed.
*
* @param afterAction the action to take after the dialog is closed
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder afterAction(DialogAfterAction afterAction);
/**
* Sets the body of the dialog.
*
* @param body the body of the dialog
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder body(List<? extends DialogBody> body);
/**
* Sets the inputs of the dialog.
*
* @param inputs the inputs of the dialog
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder inputs(List<? extends DialogInput> inputs);
/**
* Builds the dialog base.
*
* @return the built dialog base
*/
@Contract(pure = true, value = "-> new")
DialogBase build();
}
}
@@ -0,0 +1,89 @@
package io.papermc.paper.registry.data.dialog;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.action.DialogActionCallback;
import io.papermc.paper.registry.data.dialog.body.ItemDialogBody;
import io.papermc.paper.registry.data.dialog.body.PlainMessageDialogBody;
import io.papermc.paper.registry.data.dialog.input.BooleanDialogInput;
import io.papermc.paper.registry.data.dialog.input.NumberRangeDialogInput;
import io.papermc.paper.registry.data.dialog.input.SingleOptionDialogInput;
import io.papermc.paper.registry.data.dialog.input.TextDialogInput;
import io.papermc.paper.registry.data.dialog.type.ConfirmationType;
import io.papermc.paper.registry.data.dialog.type.DialogListType;
import io.papermc.paper.registry.data.dialog.type.MultiActionType;
import io.papermc.paper.registry.data.dialog.type.NoticeType;
import io.papermc.paper.registry.data.dialog.type.ServerLinksType;
import io.papermc.paper.registry.set.RegistrySet;
import java.util.List;
import java.util.Optional;
import java.util.ServiceLoader;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.event.ClickEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.Nullable;
/**
* @hidden
*/
@SuppressWarnings("MissingJavadoc")
@ApiStatus.Internal
public interface DialogInstancesProvider {
static DialogInstancesProvider instance() {
final class Holder {
static final Optional<DialogInstancesProvider> INSTANCE = ServiceLoader.load(DialogInstancesProvider.class).findFirst();
}
return Holder.INSTANCE.orElseThrow();
}
DialogBase.Builder dialogBaseBuilder(Component title);
ActionButton.Builder actionButtonBuilder(Component label);
// actions
DialogAction.CustomClickAction register(DialogActionCallback callback, ClickCallback.Options options);
DialogAction.StaticAction staticAction(ClickEvent value);
DialogAction.CommandTemplateAction commandTemplate(String template);
DialogAction.CustomClickAction customClick(Key id, @Nullable BinaryTagHolder additions);
// bodies
ItemDialogBody.Builder itemDialogBodyBuilder(ItemStack itemStack);
PlainMessageDialogBody plainMessageDialogBody(Component component);
PlainMessageDialogBody plainMessageDialogBody(Component component, int width);
// inputs
BooleanDialogInput.Builder booleanBuilder(String key, Component label);
NumberRangeDialogInput.Builder numberRangeBuilder(String key, Component label, float start, float end);
SingleOptionDialogInput.Builder singleOptionBuilder(String key, Component label, List<SingleOptionDialogInput.OptionEntry> entries);
SingleOptionDialogInput.OptionEntry singleOptionEntry(String id, @Nullable Component display, boolean initial);
TextDialogInput.Builder textBuilder(String key, Component label);
TextDialogInput.MultilineOptions multilineOptions(@Nullable Integer maxLines, @Nullable Integer height);
// types
ConfirmationType confirmation(ActionButton yesButton, ActionButton noButton);
DialogListType.Builder dialogList(RegistrySet<Dialog> dialogs);
MultiActionType.Builder multiAction(List<ActionButton> actions);
NoticeType notice();
NoticeType notice(ActionButton action);
ServerLinksType serverLinks(@Nullable ActionButton exitAction, int columns, int buttonWidth);
}
@@ -0,0 +1,75 @@
package io.papermc.paper.registry.data.dialog;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.data.dialog.type.DialogListType;
import io.papermc.paper.registry.data.dialog.type.DialogType;
import io.papermc.paper.registry.set.RegistryValueSetBuilder;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
/**
* A data-centric version-specific registry entry for the {@link io.papermc.paper.dialog.Dialog} type.
*/
@ApiStatus.NonExtendable
public interface DialogRegistryEntry {
/**
* The base dialog for this entry.
*
* @return the base dialog
*/
@Contract(pure = true)
DialogBase base();
/**
* The type of dialog for this entry.
*
* @return the dialog type
*/
@Contract(pure = true)
DialogType type();
/**
* A mutable builder for the {@link DialogRegistryEntry} plugins may change in applicable registry events.
* <p>
* The following values are required for each builder:
* <ul>
* <li>{@link #base(DialogBase)}</li>
* <li>{@link #type(DialogType)}</li>
* </ul>
*/
@ApiStatus.NonExtendable
interface Builder extends DialogRegistryEntry, RegistryBuilder<Dialog> {
/**
* Provides a builder for dialog {@link io.papermc.paper.registry.set.RegistryValueSet} which
* can be used inside {@link DialogListType}.
* <p>Not a part of the registry entry.</p>
*
* @return a new registry value set builder
*/
@Contract(value = "-> new", pure = true)
RegistryValueSetBuilder<Dialog, DialogRegistryEntry.Builder> registryValueSet();
/**
* Sets the base dialog for this entry.
*
* @param dialogBase the base dialog
* @return this builder instance
* @see DialogRegistryEntry#base()
*/
@Contract(value = "_ -> this", mutates = "this")
Builder base(DialogBase dialogBase);
/**
* Sets the specialty dialog for this entry.
*
* @param dialogType the specialty dialog
* @return this builder instance
* @see DialogRegistryEntry#type()
*/
@Contract(value = "_ -> this", mutates = "this")
Builder type(DialogType dialogType);
}
}
@@ -0,0 +1,122 @@
package io.papermc.paper.registry.data.dialog.action;
import io.papermc.paper.registry.data.dialog.DialogInstancesProvider;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.event.ClickEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.Nullable;
/**
* Represents an action that can be performed in a dialog.
*/
public sealed interface DialogAction permits DialogAction.CommandTemplateAction, DialogAction.CustomClickAction, DialogAction.StaticAction {
/**
* Creates a new command template action. The template format
* looks for {@code $(variable_name)} to substitute variables.
* {@code variable_name} should correspond to a {@link io.papermc.paper.registry.data.dialog.input.DialogInput#key()}.
*
* @param template the command template to execute
* @return a new command template action instance
*/
@Contract(pure = true, value = "_ -> new")
static CommandTemplateAction commandTemplate(final String template) {
return DialogInstancesProvider.instance().commandTemplate(template);
}
/**
* Creates a new static action that performs a click event.
*
* @param value the click event to perform
* @return a new static action instance
*/
@Contract(pure = true, value = "_ -> new")
static StaticAction staticAction(final ClickEvent value) {
return DialogInstancesProvider.instance().staticAction(value);
}
/**
* Creates a new custom click action that executes a custom action.
* Each {@link io.papermc.paper.registry.data.dialog.input.DialogInput#key()} is added
* to the compound binary tag holder.
*
* @param id the identifier of the custom action
* @param additions additional data to be sent with the action, or null if not needed
* @return a new custom all action instance
*/
@Contract(pure = true, value = "_, _ -> new")
static CustomClickAction customClick(final Key id, final @Nullable BinaryTagHolder additions) {
return DialogInstancesProvider.instance().customClick(id, additions);
}
/**
* Creates a new custom click action that executes a custom action.
*
* @param callback the custom action to execute
* @param options the options for the custom action
* @return a new custom all action instance
*/
@Contract(pure = true, value = "_, _ -> new")
static CustomClickAction customClick(final DialogActionCallback callback, final ClickCallback.Options options) {
return DialogInstancesProvider.instance().register(callback, options);
}
/**
* Represents an action that executes a command template.
*/
@ApiStatus.NonExtendable
non-sealed interface CommandTemplateAction extends DialogAction {
/**
* The command template to execute.
*
* @return the command template
*/
@Contract(pure = true)
String template();
}
/**
* Represents an action that performs a static click event.
*/
@ApiStatus.NonExtendable
non-sealed interface StaticAction extends DialogAction {
/**
* The click event to perform.
*
* @return the click event
*/
@Contract(pure = true)
ClickEvent value();
}
/**
* Represents an action that executes a custom action with additional data.
*/
@ApiStatus.NonExtendable
non-sealed interface CustomClickAction extends DialogAction {
/**
* The identifier of the custom action.
*
* @return the identifier
*/
@Contract(pure = true)
Key id();
/**
* Additional data to be sent with the action.
* This is a compound binary tag holder that can contain
* various data related to the action.
*
* @return the additional data, or null if not needed
*/
@Contract(pure = true)
@Nullable BinaryTagHolder additions();
}
}
@@ -0,0 +1,21 @@
package io.papermc.paper.registry.data.dialog.action;
import io.papermc.paper.dialog.DialogResponseView;
import net.kyori.adventure.audience.Audience;
import org.jetbrains.annotations.ApiStatus;
/**
* A callback for a dialog action.
*/
@FunctionalInterface
public interface DialogActionCallback {
/**
* Handles a dialog action.
*
* @param response the response to the action
* @param audience the audience to send the response to
*/
@ApiStatus.OverrideOnly
void accept(DialogResponseView response, Audience audience);
}
@@ -0,0 +1,9 @@
/**
* This package contains classes related to dialog actions in the Paper API.
*/
@ApiStatus.Experimental
@NullMarked
package io.papermc.paper.registry.data.dialog.action;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@@ -0,0 +1,77 @@
package io.papermc.paper.registry.data.dialog.body;
import io.papermc.paper.registry.data.dialog.DialogInstancesProvider;
import net.kyori.adventure.text.Component;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Range;
import org.jspecify.annotations.Nullable;
/**
* Represents the body of a dialog.
*/
public sealed interface DialogBody permits ItemDialogBody, PlainMessageDialogBody {
/**
* Creates an item body for a dialog.
*
* @param item the item to display in the dialog
* @param description the description of the body, or null if not set
* @param showDecorations whether to show decorations around the item
* @param showTooltip whether to show a tooltip for the item
* @param width the width of the item body
* @param height the height of the item body
* @return a new item body instance
*/
@Contract(pure = true, value = "_, _, _, _, _, _ -> new")
static ItemDialogBody item(
final ItemStack item,
final @Nullable PlainMessageDialogBody description,
final boolean showDecorations,
final boolean showTooltip,
final int width,
final int height
) {
return item(item)
.description(description)
.showDecorations(showDecorations)
.showTooltip(showTooltip)
.width(width)
.height(height)
.build();
}
/**
* Creates a new item dialog body builder.
*
* @param item the item to display in the dialog
* @return a new item dialog body builder instance
*/
@Contract(pure = true, value = "_ -> new")
static ItemDialogBody.Builder item(final ItemStack item) {
return DialogInstancesProvider.instance().itemDialogBodyBuilder(item);
}
/**
* Creates a plain message body for a dialog.
*
* @param contents the contents of the message
* @return a new plain message body instance
*/
@Contract(pure = true, value = "_, -> new")
static PlainMessageDialogBody plainMessage(final Component contents) {
return DialogInstancesProvider.instance().plainMessageDialogBody(contents);
}
/**
* Creates a plain message body for a dialog.
*
* @param contents the contents of the message
* @param width the width of the message body
* @return a new plain message body instance
*/
@Contract(pure = true, value = "_, _ -> new")
static PlainMessageDialogBody plainMessage(final Component contents, final @Range(from = 1, to = 1024) int width) {
return DialogInstancesProvider.instance().plainMessageDialogBody(contents, width);
}
}
@@ -0,0 +1,124 @@
package io.papermc.paper.registry.data.dialog.body;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Range;
import org.jspecify.annotations.Nullable;
/**
* An item body for a dialog.
* <p>Created via {@link DialogBody#item(ItemStack, PlainMessageDialogBody, boolean, boolean, int, int)}</p>
*/
@ApiStatus.NonExtendable
public non-sealed interface ItemDialogBody extends DialogBody {
/**
* The item to display in the dialog.
*
* @return the item stack
*/
@Contract(pure = true)
ItemStack item();
/**
* The description of the body, or null if not set.
*
* @return the description body
*/
@Contract(pure = true)
@Nullable PlainMessageDialogBody description();
/**
* Whether to show decorations around the item.
* <p>Decorations include damage, itemstack count, etc.</p>
*
* @return true if decorations should be shown
*/
@Contract(pure = true)
boolean showDecorations();
/**
* Whether to show a tooltip for the item.
*
* @return true if a tooltip should be shown
*/
@Contract(pure = true)
boolean showTooltip();
/**
* The width of the item body.
*
* @return the width
*/
@Contract(pure = true)
int width();
/**
* The height of the item body.
*
* @return the height
*/
@Contract(pure = true)
int height();
/**
* A builder for an item dialog body.
*/
@ApiStatus.NonExtendable
interface Builder {
/**
* Sets the description of the item dialog body, or null if not set.
*
* @param description the description of the body, or null
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder description(@Nullable PlainMessageDialogBody description);
/**
* Sets whether to show decorations around the item.
*
* @param showDecorations true to show decorations, false otherwise
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder showDecorations(boolean showDecorations);
/**
* Sets whether to show a tooltip for the item.
*
* @param showTooltip true to show a tooltip, false otherwise
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder showTooltip(boolean showTooltip);
/**
* Sets the width of the item body.
*
* @param width the width, must be between 1 and 256
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder width(@Range(from = 1, to = 256) int width);
/**
* Sets the height of the item body.
*
* @param height the height, must be between 1 and 256
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder height(@Range(from = 1, to = 256) int height);
/**
* Builds a new instance of {@link ItemDialogBody}.
*
* @return a new item dialog body instance
*/
@Contract(value = "-> new", pure = true)
ItemDialogBody build();
}
}
@@ -0,0 +1,28 @@
package io.papermc.paper.registry.data.dialog.body;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Range;
/**
* A plain message body for a dialog.
* <p>Created via {@link DialogBody#plainMessage(Component, int)}</p>
*/
public non-sealed interface PlainMessageDialogBody extends DialogBody {
/**
* The contents of the plain message body.
*
* @return the component contents
*/
@Contract(pure = true)
Component contents();
/**
* The width of the plain message body.
*
* @return the width
*/
@Contract(pure = true)
@Range(from = 1, to = 1024) int width();
}
@@ -0,0 +1,9 @@
/**
* This package contains classes related to dialog bodies in the Paper API.
*/
@ApiStatus.Experimental
@NullMarked
package io.papermc.paper.registry.data.dialog.body;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@@ -0,0 +1,88 @@
package io.papermc.paper.registry.data.dialog.input;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
/**
* A boolean dialog input.
* <p>Created via {@link DialogInput#bool(String, Component, boolean, String, String)}</p>
*/
@ApiStatus.NonExtendable
public non-sealed interface BooleanDialogInput extends DialogInput {
/**
* The label for the input.
*
* @return the label component
*/
@Contract(pure = true)
Component label();
/**
* The initial value of the input.
*
* @return true if the input is initially true, false otherwise
*/
@Contract(pure = true)
boolean initial();
/**
* The input's value in a template when the value is true.
*
* @return the string to use when the input is true
*/
@Contract(pure = true)
String onTrue();
/**
* The input's value in a template when the value is false.
*
* @return the string to use when the input is false
*/
@Contract(pure = true)
String onFalse();
/**
* A builder for a boolean dialog input.
* <p>Created via {@link DialogInput#bool(String, Component)}</p>
*/
@ApiStatus.NonExtendable
interface Builder {
/**
* Sets the initial value of the input.
*
* @param initial true if the input is initially true, false otherwise
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder initial(boolean initial);
/**
* Sets the input's value in a template when the value is true.
*
* @param onTrue the string to use when the input is true
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder onTrue(String onTrue);
/**
* Sets the input's value in a template when the value is false.
*
* @param onFalse the string to use when the input is false
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder onFalse(String onFalse);
/**
* Builds the instance with the configured values.
*
* @return a new instance
*/
@Contract(value = "-> new", pure = true)
BooleanDialogInput build();
}
}
@@ -0,0 +1,142 @@
package io.papermc.paper.registry.data.dialog.input;
import io.papermc.paper.registry.data.dialog.DialogInstancesProvider;
import java.util.List;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.Nullable;
/**
* Represents an input for dialog.
*/
public sealed interface DialogInput permits BooleanDialogInput, NumberRangeDialogInput, SingleOptionDialogInput, TextDialogInput {
/**
* Creates a boolean dialog input.
*
* @param key the key identifier for the input
* @param label the label for the input
* @param initial the initial value of the input
* @param onTrue the input's value in a template when the value is true
* @param onFalse the input's value in a template when the value is false
* @return a new boolean dialog input instance
*/
@Contract(pure = true, value = "_, _, _, _, _ -> new")
static BooleanDialogInput bool(final String key, final Component label, final boolean initial, final String onTrue, final String onFalse) {
return bool(key, label)
.initial(initial)
.onTrue(onTrue)
.onFalse(onFalse)
.build();
}
/**
* Creates a new builder for a boolean dialog input.
*
* @param key the key identifier for the input
* @param label the label for the input
* @return a new builder instance
*/
@Contract(pure = true, value = "_, _ -> new")
static BooleanDialogInput.Builder bool(final String key, final Component label) {
return DialogInstancesProvider.instance().booleanBuilder(key, label);
}
/**
* Creates a number range dialog input.
*
* @param key the key identifier for the input
* @param width the width of the input
* @param label the label for the input
* @param labelFormat the format for the label (a translation key or format string)
* @param start the start of the range
* @param end the end of the range
* @param initial the initial value, or null if not set
* @param step the step size, or null if not set
* @return a new number range dialog input instance
*/
@Contract(pure = true, value = "_, _, _, _, _, _, _, _ -> new")
static NumberRangeDialogInput numberRange(final String key, final int width, final Component label, final String labelFormat, final float start, final float end, final @Nullable Float initial, final @Nullable Float step) {
return numberRange(key, label, start, end).width(width).labelFormat(labelFormat).initial(initial).step(step).build();
}
/**
* Creates a new builder for a number range dialog input.
*
* @param key the key identifier for the input
* @param label the label for the input
* @param start the start of the range
* @param end the end of the range
* @return a new builder instance
*/
@Contract(value = "_, _, _, _ -> new", pure = true)
static NumberRangeDialogInput.Builder numberRange(final String key, final Component label, final float start, final float end) {
return DialogInstancesProvider.instance().numberRangeBuilder(key, label, start, end);
}
/**
* Creates a single option dialog input (radio input).
*
* @param key the key identifier for the input
* @param width the width of the input
* @param entries the list of options for the input
* @param label the label for the input
* @param labelVisible whether the label should be visible
* @return a new single option dialog input instance
*/
@Contract(pure = true, value = "_, _, _, _, _ -> new")
static SingleOptionDialogInput singleOption(final String key, final int width, final List<SingleOptionDialogInput.OptionEntry> entries, final Component label, final boolean labelVisible) {
return singleOption(key, label, entries).width(width).labelVisible(labelVisible).build();
}
/**
* Creates a new builder for a single option dialog input.
*
* @param key the key identifier for the input
* @param label the label for the input
* @param entries the list of options for the input
* @return a new builder instance
*/
@Contract(value = "_, _, _ -> new", pure = true)
static SingleOptionDialogInput.Builder singleOption(final String key, final Component label, final List<SingleOptionDialogInput.OptionEntry> entries) {
return DialogInstancesProvider.instance().singleOptionBuilder(key, label, entries);
}
/**
* Creates a text dialog input.
*
* @param key the key identifier for the input
* @param width the width of the input
* @param label the label for the input
* @param labelVisible whether the label should be visible
* @param initial the initial value of the input
* @param maxLength the maximum length of the input
* @param multilineOptions the multiline options
* @return a new text dialog input instance
*/
@Contract(pure = true, value = "_, _, _, _, _, _, _ -> new")
static TextDialogInput text(final String key, final int width, final Component label, final boolean labelVisible, final String initial, final int maxLength, final TextDialogInput.@Nullable MultilineOptions multilineOptions) {
return text(key, label).width(width).labelVisible(labelVisible).initial(initial).maxLength(maxLength).multiline(multilineOptions).build();
}
/**
* Creates a new builder for a text dialog input.
*
* @param key the key identifier for the input
* @param label the label for the input
* @return a new builder instance
*/
@Contract(value = "_, _ -> new", pure = true)
static TextDialogInput.Builder text(final String key, final Component label) {
return DialogInstancesProvider.instance().textBuilder(key, label);
}
/**
* Gets the key for this input.
* <p>Used in dialog actions to identify this dialog input's value</p>
*
* @return the key
*/
@Contract(pure = true, value = " -> new")
String key();
}
@@ -0,0 +1,126 @@
package io.papermc.paper.registry.data.dialog.input;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.index.qual.Positive;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
/**
* A number range dialog input.
* <p>Created via {@link DialogInput#numberRange(String, int, Component, String, float, float, Float, Float)}</p>
*/
@ApiStatus.NonExtendable
public non-sealed interface NumberRangeDialogInput extends DialogInput {
/**
* The width of the input.
*
* @return the width
*/
@Contract(pure = true)
@Range(from = 1, to = 1024) int width();
/**
* The label for the input.
*
* @return the label component
*/
@Contract(pure = true)
Component label();
/**
* The format for the label, which can be a translation key or a format string.
* <p>Example: {@code "%s: %s"} or {@code "options.generic_value"}</p>
*
* @return the label format
*/
@Contract(pure = true)
String labelFormat();
/**
* The start of the range.
*
* @return the start value
*/
@Contract(pure = true)
float start();
/**
* The end of the range.
*
* @return the end value
*/
@Contract(pure = true)
float end();
/**
* The initial value of the input, or null if not set.
*
* @return the initial value, or null
*/
@Contract(pure = true)
@Nullable Float initial();
/**
* The step size for the input, or null if not set.
*
* @return the step size, or null
*/
@Contract(pure = true)
@Positive @Nullable Float step();
/**
* A builder for creating instances of {@link NumberRangeDialogInput}.
* <p>Created via {@link DialogInput#numberRange(String, Component, float, float)}</p>
*/
@ApiStatus.NonExtendable
interface Builder {
/**
* Sets the width of the input.
*
* @param width the width
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder width(@Range(from = 1, to = 1024) int width);
/**
* Sets the format for the label.
* <p>Example: {@code "%s: %s"} or {@code "options.generic_value"}</p>
*
* @param labelFormat the label format
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder labelFormat(String labelFormat);
/**
* Sets the initial value for the input.
*
* @param initial the initial value, or null if not set
* @return this builder
*/
@Contract(value = "_ -> this", pure = true)
Builder initial(@Nullable Float initial);
/**
* Sets the step of the range.
*
* @param step the step size, or null if not set
* @return this builder
*/
@Contract(value = "_ -> this", pure = true)
Builder step(@Positive @Nullable Float step);
/**
* Builds the instance with the configured values.
*
* @return a new instance
*/
@Contract(pure = true, value = "-> new")
NumberRangeDialogInput build();
}
}
@@ -0,0 +1,130 @@
package io.papermc.paper.registry.data.dialog.input;
import io.papermc.paper.registry.data.dialog.DialogInstancesProvider;
import java.util.List;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
import org.jetbrains.annotations.Unmodifiable;
/**
* A single option dialog input.
* <p>Created via {@link DialogInput#singleOption(String, int, List, Component, boolean)}</p>
*/
@ApiStatus.NonExtendable
public non-sealed interface SingleOptionDialogInput extends DialogInput {
/**
* The width of the input.
*
* @return the width
*/
@Contract(pure = true)
@Range(from = 1, to = 1024) int width();
/**
* The list of options for the input.
*
* @return the list of option entries
*/
@Contract(pure = true)
@Unmodifiable List<OptionEntry> entries();
/**
* The label for the input.
*
* @return the label component
*/
@Contract(pure = true)
Component label();
/**
* Whether the label should be visible.
*
* @return true if the label is visible, false otherwise
*/
@Contract(pure = true)
boolean labelVisible();
/**
* Represents a single option entry in a single option dialog input.
* <p>Only 1 option is allowed to have initial selected.</p>
*/
@ApiStatus.NonExtendable
interface OptionEntry {
/**
* Creates a new option entry.
*
* @param id the unique identifier for the option
* @param display the display name for the option, or null if not set
* @param initial whether this option is initially selected
* @return a new option entry instance
*/
@Contract(pure = true, value = "_, _, _ -> new")
static OptionEntry create(final String id, final @Nullable Component display, final boolean initial) {
return DialogInstancesProvider.instance().singleOptionEntry(id, display, initial);
}
/**
* The unique identifier for the option.
*
* @return the option ID
*/
@Contract(pure = true)
String id();
/**
* The display name for the option, or null if not set.
*
* @return the display component, or null
*/
@Contract(pure = true)
@Nullable Component display();
/**
* Whether this option is initially selected.
* <p>Only 1 option is allowed to have initial selected.</p>
*
* @return true if the option is initially selected, false otherwise
*/
@Contract(pure = true)
boolean initial();
}
/**
* A builder for creating a {@link SingleOptionDialogInput}.
* <p>Created via {@link DialogInput#singleOption(String, Component, List)}</p>
*/
@ApiStatus.NonExtendable
interface Builder {
/**
* Sets the width of the input.
*
* @param width the width
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder width(@Range(from = 1, to = 1024) int width);
/**
* Sets whether the label should be visible.
*
* @param labelVisible whether the label should be visible
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder labelVisible(boolean labelVisible);
/**
* Builds the {@link SingleOptionDialogInput}.
*
* @return the built dialog input
*/
@Contract(value = "-> new", pure = true)
SingleOptionDialogInput build();
}
}
@@ -0,0 +1,159 @@
package io.papermc.paper.registry.data.dialog.input;
import io.papermc.paper.registry.data.dialog.DialogInstancesProvider;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Range;
import org.jspecify.annotations.Nullable;
/**
* A text dialog input.
* <p>Created via {@link DialogInput#text(String, int, Component, boolean, String, int, MultilineOptions)}</p>
*/
@ApiStatus.NonExtendable
public non-sealed interface TextDialogInput extends DialogInput {
/**
* The width of the input.
*
* @return the width of the input
*/
@Contract(pure = true)
@Range(from = 1, to = 1024) int width();
/**
* The label for the input.
*
* @return the label component
*/
@Contract(pure = true)
Component label();
/**
* Whether the label should be visible.
*
* @return true if the label is visible, false otherwise
*/
@Contract(pure = true)
boolean labelVisible();
/**
* The initial value of the input.
*
* @return the initial text
*/
@Contract(pure = true)
String initial();
/**
* The format for the label (a translation key or format string).
*
* @return the label format
*/
@Contract(pure = true)
@Range(from = 1, to = Integer.MAX_VALUE) int maxLength();
/**
* The multiline options for the input, or null if not set.
*
* @return the multiline options
*/
@Contract(pure = true)
@Nullable MultilineOptions multiline();
/**
* Represents the multiline options for a text dialog input.
*/
@ApiStatus.NonExtendable
interface MultilineOptions {
/**
* Creates a new multiline options instance.
*
* @param maxLines the maximum number of lines, or null if not set
* @param height the height of the input, or null if not set
* @return a new MultilineOptions instance
*/
static MultilineOptions create(final @Nullable Integer maxLines, final @Nullable Integer height) {
return DialogInstancesProvider.instance().multilineOptions(maxLines, height);
}
/**
* Gets the maximum number of lines.
*
* @return the maximum number of lines, or null if not set
*/
@Contract(pure = true)
@Nullable Integer maxLines();
/**
* Gets the height of the input.
*
* @return the height of the input, or null if not set
*/
@Contract(pure = true)
@Nullable Integer height();
}
/**
* A builder for a text dialog input.
* <p>Created via {@link DialogInput#text(String, Component)}</p>
*/
@ApiStatus.NonExtendable
interface Builder {
/**
* Sets the width of the input.
*
* @param width the width of the input
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder width(final @Range(from = 1, to = 1024) int width);
/**
* Sets whether the label should be visible.
*
* @param labelVisible true if the label should be visible, false otherwise
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder labelVisible(final boolean labelVisible);
/**
* Sets the initial value of the input.
*
* @param initial the initial value of the input
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder initial(final String initial);
/**
* Sets the maximum length of the input.
*
* @param maxLength the maximum length of the input
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder maxLength(final @Range(from = 1, to = Integer.MAX_VALUE) int maxLength);
/**
* Sets the multiline options for the input.
*
* @param multiline the multiline options
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder multiline(final @Nullable MultilineOptions multiline);
/**
* Builds the text dialog input.
*
* @return the text dialog input
*/
@Contract(value = "-> new", pure = true)
TextDialogInput build();
}
}
@@ -0,0 +1,9 @@
/**
* Dialog inputs for Paper API.
*/
@ApiStatus.Experimental
@NullMarked
package io.papermc.paper.registry.data.dialog.input;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@@ -0,0 +1,6 @@
@NullMarked
@ApiStatus.Experimental
package io.papermc.paper.registry.data.dialog;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@@ -0,0 +1,30 @@
package io.papermc.paper.registry.data.dialog.type;
import io.papermc.paper.registry.data.dialog.ActionButton;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
/**
* Represents a confirmation dialog.
* This interface defines the structure for a confirmation dialog with "confirm" and "deny" buttons.
* @see DialogType#confirmation(ActionButton, ActionButton)
*/
@ApiStatus.NonExtendable
public non-sealed interface ConfirmationType extends DialogType {
/**
* Gets the button for confirming the action.
*
* @return the confirmation button
*/
@Contract(pure = true)
ActionButton yesButton();
/**
* Gets the button for denying the action.
*
* @return the denial button
*/
@Contract(pure = true)
ActionButton noButton();
}
@@ -0,0 +1,91 @@
package io.papermc.paper.registry.data.dialog.type;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.set.RegistrySet;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Range;
import org.jspecify.annotations.Nullable;
/**
* Represents a dialog that displays a list of dialogs.
* @see DialogType#dialogList(RegistrySet, ActionButton, int, int)
*/
@ApiStatus.NonExtendable
public non-sealed interface DialogListType extends DialogType {
/**
* Returns the set of dialogs to display in the dialog list.
*
* @return the set of dialogs
*/
@Contract(pure = true)
RegistrySet<Dialog> dialogs();
/**
* Returns the action button to exit the dialog, or null if there is no exit action.
*
* @return the exit action button, or null
*/
@Contract(pure = true)
@Nullable ActionButton exitAction();
/**
* Returns the number of columns to display in the dialog list.
*
* @return the number of columns
*/
@Contract(pure = true)
@Range(from = 1, to = Integer.MAX_VALUE) int columns();
/**
* Returns the width of each button in the dialog list.
*
* @return the width of the buttons
*/
@Contract(pure = true)
@Range(from = 1, to = 1024) int buttonWidth();
/**
* A builder for creating a dialog list type.
*/
@ApiStatus.NonExtendable
interface Builder {
/**
* Sets the action button to exit the dialog, or null if there is no exit action.
*
* @param exitAction the exit action button, or null
* @return the builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder exitAction(final @Nullable ActionButton exitAction);
/**
* Sets the number of columns to display in the dialog list.
*
* @param columns the number of columns
* @return the builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder columns(final @Range(from = 1, to = Integer.MAX_VALUE) int columns);
/**
* Sets the width of each button in the dialog list.
*
* @param buttonWidth the width of the buttons
* @return the builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder buttonWidth(final @Range(from = 1, to = 1024) int buttonWidth);
/**
* Builds the dialog list type.
*
* @return the built dialog list type
*/
@Contract(value = "-> new", pure = true)
DialogListType build();
}
}
@@ -0,0 +1,110 @@
package io.papermc.paper.registry.data.dialog.type;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.data.dialog.DialogInstancesProvider;
import io.papermc.paper.registry.set.RegistrySet;
import java.util.List;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.Nullable;
/**
* Represents a type of dialog.
*/
public sealed interface DialogType permits ConfirmationType, DialogListType, MultiActionType, NoticeType, ServerLinksType {
/**
* Creates a confirmation dialog with the specified yes and no buttons.
*
* @param yesButton the button to confirm the action
* @param noButton the button to cancel the action
* @return a new instance
*/
@Contract(value = "_, _ -> new", pure = true)
static ConfirmationType confirmation(final ActionButton yesButton, final ActionButton noButton) {
return DialogInstancesProvider.instance().confirmation(yesButton, noButton);
}
/**
* Creates a dialog list dialog with the specified dialogs, exit action, columns, and button width.
*
* @param dialogs the set of dialogs to display
* @param exitAction the action button to exit the dialog
* @param columns the number of columns to display in the dialog
* @param buttonWidth the width of each button in the dialog
* @return a new instance
*/
@Contract(value = "_, _, _, _ -> new", pure = true)
static DialogListType dialogList(final RegistrySet<Dialog> dialogs, final @Nullable ActionButton exitAction, final int columns, final int buttonWidth) {
return dialogList(dialogs).exitAction(exitAction).columns(columns).buttonWidth(buttonWidth).build();
}
/**
* Creates a dialog list builder with the specified dialogs.
*
* @param dialogs the set of dialogs to display
* @return a new builder instance
*/
@Contract(value = "_ -> new", pure = true)
static DialogListType.Builder dialogList(final RegistrySet<Dialog> dialogs) {
return DialogInstancesProvider.instance().dialogList(dialogs);
}
/**
* Creates a multi-action dialog with the specified actions, exit action, and number of columns.
*
* @param actions the list of action buttons to display
* @param exitAction the action button to exit the dialog
* @param columns the number of columns to display in the dialog
* @return a new instance
*/
@Contract(value = "_, _, _ -> new", pure = true)
static MultiActionType multiAction(final List<ActionButton> actions, final @Nullable ActionButton exitAction, final int columns) {
return multiAction(actions).exitAction(exitAction).columns(columns).build();
}
/**
* Creates a multi-action dialog builder with the specified actions.
*
* @param actions the list of action buttons to display
* @return a new builder instance
*/
@Contract(value = "_ -> new", pure = true)
static MultiActionType.Builder multiAction(final List<ActionButton> actions) {
return DialogInstancesProvider.instance().multiAction(actions);
}
/**
* Creates a notice dialog with the default action button.
*
* @return a new instance
*/
@Contract(value = "-> new", pure = true)
static NoticeType notice() {
return DialogInstancesProvider.instance().notice();
}
/**
* Creates a notice dialog with the specified action button.
*
* @param action the action button to display in the notice
* @return a new instance
*/
@Contract(value = "_ -> new", pure = true)
static NoticeType notice(final ActionButton action) {
return DialogInstancesProvider.instance().notice(action);
}
/**
* Creates a server links dialog with the specified exit action, number of columns, and button width.
*
* @param exitAction the action button to exit the dialog
* @param columns the number of columns to display in the dialog
* @param buttonWidth the width of each button in the dialog
* @return a new instance
*/
@Contract(value = "_, _, _ -> new", pure = true)
static ServerLinksType serverLinks(final @Nullable ActionButton exitAction, final int columns, final int buttonWidth) {
return DialogInstancesProvider.instance().serverLinks(exitAction, columns, buttonWidth);
}
}
@@ -0,0 +1,75 @@
package io.papermc.paper.registry.data.dialog.type;
import io.papermc.paper.registry.data.dialog.ActionButton;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
import org.jetbrains.annotations.Unmodifiable;
/**
* Represents a dialog that allows multiple actions to be performed.
* This dialog is used to create dialogs with multiple action buttons, allowing users to choose from several options.
* @see DialogType#multiAction(List, ActionButton, int)
*/
@ApiStatus.NonExtendable
public non-sealed interface MultiActionType extends DialogType {
/**
* Returns the list of action buttons available in this multi-action dialog.
*
* @return an unmodifiable list of action buttons
*/
@Contract(pure = true)
@Unmodifiable List<ActionButton> actions();
/**
* Returns the action button to exit the dialog, or null if there is no exit action.
*
* @return the exit action button, or null
*/
@Contract(pure = true)
@Nullable ActionButton exitAction();
/**
* Returns the number of columns to display in the dialog.
*
* @return the number of columns
*/
@Contract(pure = true)
@Range(from = 1, to = Integer.MAX_VALUE) int columns();
/**
* A builder for creating a multi-action dialog.
*/
@ApiStatus.NonExtendable
interface Builder {
/**
* Sets the action button to exit the dialog, or null if there is no exit action.
*
* @param exitAction the exit action button, or null
* @return the builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder exitAction(final @Nullable ActionButton exitAction);
/**
* Sets the number of columns to display in the dialog.
*
* @param columns the number of columns
* @return the builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder columns(final @Range(from = 1, to = Integer.MAX_VALUE) int columns);
/**
* Builds the multi-action dialog.
*
* @return a new instance
*/
@Contract(value = "-> new", pure = true)
MultiActionType build();
}
}
@@ -0,0 +1,21 @@
package io.papermc.paper.registry.data.dialog.type;
import io.papermc.paper.registry.data.dialog.ActionButton;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
/**
* Represents a notice dialog.
* @see DialogType#notice(ActionButton)
*/
@ApiStatus.NonExtendable
public non-sealed interface NoticeType extends DialogType {
/**
* Returns the action button associated with this notice type.
*
* @return the action button
*/
@Contract(pure = true)
ActionButton action();
}
@@ -0,0 +1,40 @@
package io.papermc.paper.registry.data.dialog.type;
import io.papermc.paper.registry.data.dialog.ActionButton;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Range;
import org.jspecify.annotations.Nullable;
/**
* Represents a server links dialog that displays links.
* @see DialogType#serverLinks(ActionButton, int, int)
*/
@ApiStatus.NonExtendable
public non-sealed interface ServerLinksType extends DialogType {
/**
* Returns the action button to exit the dialog, or null if there is no exit action.
*
* @return the exit action button, or null
*/
@Contract(pure = true)
@Nullable ActionButton exitAction();
/**
* Returns the number of columns to display in the server links dialog.
*
* @return the number of columns
*/
@Contract(pure = true)
@Range(from = 1, to = Integer.MAX_VALUE) int columns();
/**
* Returns the width of each button in the server links dialog.
*
* @return the width of the buttons
*/
@Contract(pure = true)
@Range(from = 1, to = 1024) int buttonWidth();
}
@@ -0,0 +1,7 @@
/**
* Dialog types for the Paper API.
*/
@NullMarked
package io.papermc.paper.registry.data.dialog.type;
import org.jspecify.annotations.NullMarked;
@@ -1,5 +1,6 @@
package io.papermc.paper.registry.event;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.BannerPatternRegistryEntry;
import io.papermc.paper.registry.data.CatTypeRegistryEntry;
@@ -14,6 +15,7 @@ import io.papermc.paper.registry.data.JukeboxSongRegistryEntry;
import io.papermc.paper.registry.data.PaintingVariantRegistryEntry;
import io.papermc.paper.registry.data.PigVariantRegistryEntry;
import io.papermc.paper.registry.data.WolfVariantRegistryEntry;
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
import org.bukkit.Art;
import org.bukkit.GameEvent;
import org.bukkit.JukeboxSong;
@@ -51,6 +53,7 @@ public final class RegistryEvents {
public static final RegistryEventProvider<Chicken.Variant, ChickenVariantRegistryEntry.Builder> CHICKEN_VARIANT = create(RegistryKey.CHICKEN_VARIANT);
public static final RegistryEventProvider<Cow.Variant, CowVariantRegistryEntry.Builder> COW_VARIANT = create(RegistryKey.COW_VARIANT);
public static final RegistryEventProvider<Pig.Variant, PigVariantRegistryEntry.Builder> PIG_VARIANT = create(RegistryKey.PIG_VARIANT);
public static final RegistryEventProvider<Dialog, DialogRegistryEntry.Builder> DIALOG = create(RegistryKey.DIALOG);
// End generate - RegistryEvents
private RegistryEvents() {
@@ -1,17 +1,29 @@
package io.papermc.paper.registry.set;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.tag.Tag;
import io.papermc.paper.registry.tag.TagKey;
import java.util.Collection;
import java.util.Iterator;
import io.papermc.paper.registry.tag.TagKey;
import org.bukkit.Keyed;
import org.bukkit.Registry;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
@ApiStatus.Experimental
@NullMarked
/**
* Represents a collection tied to a registry.
* <p>
* There are 2 types of registry key sets:
* <ul>
* <li>{@link Tag} which is a tag from vanilla or a datapack.
* These are obtained via {@link org.bukkit.Registry#getTag(io.papermc.paper.registry.tag.TagKey)}.</li>
* <li>{@link RegistryKeySet} which is a set of keys linked to values that are present in the registry. These are
* created via {@link #keySet(RegistryKey, Iterable)} or {@link #keySetFromValues(RegistryKey, Iterable)}.</li>
* </ul>
*
* @param <T> registry value type
*/
@ApiStatus.NonExtendable
public non-sealed interface RegistryKeySet<T extends Keyed> extends Iterable<TypedKey<T>>, RegistrySet<T> { // TODO remove Keyed
@@ -12,12 +12,9 @@ import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@ApiStatus.Internal
@NullMarked
record RegistryKeySetImpl<T extends @Nullable Keyed>(RegistryKey<T> registryKey, List<TypedKey<T>> values) implements RegistryKeySet<T> { // TODO remove Keyed
record RegistryKeySetImpl<T extends Keyed>(RegistryKey<T> registryKey, List<TypedKey<T>> values) implements RegistryKeySet<T> { // TODO remove Keyed
static <T extends Keyed> RegistryKeySet<T> create(final RegistryKey<T> registryKey, final Iterable<? extends T> values) { // TODO remove Keyed
final Registry<T> registry = RegistryAccess.registryAccess().getRegistry(registryKey);
@@ -5,43 +5,38 @@ import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.tag.Tag;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NullMarked;
/**
* Represents a collection tied to a registry.
* <p>
* There are 2<!--3--> types of registry sets:
* There are 3 types of registry sets:
* <ul>
* <li>{@link Tag} which is a tag from vanilla or a datapack.
* These are obtained via {@link org.bukkit.Registry#getTag(io.papermc.paper.registry.tag.TagKey)}.</li>
* <li>{@link RegistryKeySet} which is a set of of keys linked to values that are present in the registry. These are
* <li>{@link RegistryKeySet} which is a set of keys linked to values that are present in the registry. These are
* created via {@link #keySet(RegistryKey, Iterable)} or {@link #keySetFromValues(RegistryKey, Iterable)}.</li>
* <!-- <li>{@link RegistryValueSet} which is a set of values which are anonymous (don't have keys in the registry). These are
* created via {@link #valueSet(RegistryKey, Iterable)}.</li>-->
* <li>{@link RegistryValueSet} which is a set of values which are anonymous (don't have keys in the registry). These are
* created via {@link #valueSet(RegistryKey, Iterable)}.</li>
* </ul>
*
* @param <T> registry value type
*/
@ApiStatus.Experimental
@NullMarked
public sealed interface RegistrySet<T> permits RegistryKeySet, RegistryValueSet {
// TODO uncomment when direct holder sets need to be exposed to the API
// /**
// * Creates a {@link RegistryValueSet} from anonymous values.
// * <p>All values provided <b>must not</b> have keys in the given registry.</p>
// *
// * @param registryKey the registry key for the type of these values
// * @param values the values
// * @return a new registry set
// * @param <T> the type of the values
// */
// @Contract(value = "_, _ -> new", pure = true)
// static <T> RegistryValueSet<T> valueSet(final RegistryKey<T> registryKey, final Iterable<? extends T> values) {
// return RegistryValueSetImpl.create(registryKey, values);
// }
/**
* Creates a {@link RegistryValueSet} from anonymous values.
* <p>All values provided <b>must not</b> have keys in the given registry.</p>
*
* @param registryKey the registry key for the type of these values
* @param values the values
* @return a new registry set
* @param <T> the type of the values
*/
@Contract(value = "_, _ -> new", pure = true)
static <T> RegistryValueSet<T> valueSet(final RegistryKey<T> registryKey, final Iterable<? extends T> values) {
return RegistryValueSetImpl.create(registryKey, values);
}
/**
* Creates a {@link RegistryKeySet} from registry-backed values.
@@ -1,19 +1,18 @@
package io.papermc.paper.registry.set;
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
import java.util.Collection;
import java.util.Iterator;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
/**
* A collection of anonymous values relating to a registry. These
* are values of the same type as the registry, but will not be found
* in the registry, hence, anonymous.
* in the registry, hence, anonymous. Created via {@link RegistrySet#valueSet(io.papermc.paper.registry.RegistryKey, Iterable)} or
* in the context of a {@link io.papermc.paper.registry.RegistryBuilder},
* there are methods to create them like {@link DialogRegistryEntry.Builder#registryValueSet()}.
* @param <T> registry value type
*/
@ApiStatus.Experimental
@NullMarked
public sealed interface RegistryValueSet<T> extends Iterable<T>, RegistrySet<T> permits RegistryValueSetImpl {
@Override
@@ -0,0 +1,31 @@
package io.papermc.paper.registry.set;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryBuilderFactory;
import java.util.function.Consumer;
import org.jetbrains.annotations.ApiStatus;
/**
* A builder for a {@link RegistryValueSet}.
*
* @param <API> the API type
* @param <ENTRY_BUILDER> the type of the entry builder,
*/
@ApiStatus.NonExtendable
public interface RegistryValueSetBuilder<API, ENTRY_BUILDER extends RegistryBuilder<API>> {
/**
* Adds a value to the registry value set.
*
* @param builder the builder for the value to add
* @return this builder for chaining
*/
RegistryValueSetBuilder<API, ENTRY_BUILDER> add(Consumer<RegistryBuilderFactory<API, ? extends ENTRY_BUILDER>> builder);
/**
* Builds the {@link RegistryValueSet}.
*
* @return the built registry value set
*/
RegistryValueSet<API> build();
}
@@ -4,10 +4,8 @@ import com.google.common.collect.Lists;
import io.papermc.paper.registry.RegistryKey;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@ApiStatus.Internal
@NullMarked
record RegistryValueSetImpl<T>(RegistryKey<T> registryKey, List<T> values) implements RegistryValueSet<T> {
RegistryValueSetImpl {
@@ -0,0 +1,11 @@
/**
* This package contains the API for registry sets in Paper.
* <p>
* Registry sets are collections of keys or inlined values of a type that has a registry.
*/
@ApiStatus.Experimental
@NullMarked
package io.papermc.paper.registry.set;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@@ -1,5 +1,9 @@
package org.bukkit.entity;
import io.papermc.paper.connection.PlayerGameConnection;
import io.papermc.paper.entity.LookAnchor;
import io.papermc.paper.entity.PlayerGiveResult;
import io.papermc.paper.math.Position;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.time.Duration;
@@ -10,10 +14,6 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import io.papermc.paper.connection.PlayerGameConnection;
import io.papermc.paper.entity.LookAnchor;
import io.papermc.paper.entity.PlayerGiveResult;
import io.papermc.paper.math.Position;
import org.bukkit.BanEntry;
import org.bukkit.DyeColor;
import org.bukkit.Effect;