mirror of
https://github.com/PaperMC/Paper.git
synced 2025-07-26 09:42:06 -07:00
Dialog API (#12671)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
47
paper-api/src/main/java/io/papermc/paper/dialog/Dialog.java
Normal file
47
paper-api/src/main/java/io/papermc/paper/dialog/Dialog.java
Normal file
@@ -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;
|
||||
|
@@ -23,6 +23,7 @@ import io.papermc.generator.rewriter.types.simple.trial.VillagerProfessionRewrit
|
||||
import io.papermc.generator.types.goal.MobGoalNames;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.typewriter.preset.EnumCloneRewriter;
|
||||
import io.papermc.typewriter.preset.model.EnumValue;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
@@ -184,6 +185,7 @@ public final class Rewriters {
|
||||
.register("ChickenVariant", Chicken.Variant.class, new RegistryFieldRewriter<>(Registries.CHICKEN_VARIANT, "getVariant"))
|
||||
.register("CowVariant", Cow.Variant.class, new RegistryFieldRewriter<>(Registries.COW_VARIANT, "getVariant"))
|
||||
.register("PigVariant", Pig.Variant.class, new RegistryFieldRewriter<>(Registries.PIG_VARIANT, "getVariant"))
|
||||
.register("Dialog", Dialog.class, new RegistryFieldRewriter<>(Registries.DIALOG, "getDialog"))
|
||||
.register("MemoryKey", MemoryKey.class, new MemoryKeyRewriter())
|
||||
// .register("ItemType", ItemType.class, new ItemTypeRewriter()) - disable for now, lynx want the generic type
|
||||
.register("BlockType", BlockType.class, new BlockTypeRewriter())
|
||||
|
@@ -3,6 +3,7 @@ package io.papermc.generator.registry;
|
||||
import io.papermc.generator.utils.ClassHelper;
|
||||
import io.papermc.paper.datacomponent.DataComponentType;
|
||||
import io.papermc.paper.datacomponent.DataComponentTypes;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.registry.data.BannerPatternRegistryEntry;
|
||||
import io.papermc.paper.registry.data.CatTypeRegistryEntry;
|
||||
import io.papermc.paper.registry.data.ChickenVariantRegistryEntry;
|
||||
@@ -17,6 +18,7 @@ import io.papermc.paper.registry.data.PaintingVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PigVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.SoundEventRegistryEntry;
|
||||
import io.papermc.paper.registry.data.WolfVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
@@ -34,6 +36,7 @@ import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.dialog.Dialogs;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.damagesource.DamageTypes;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
@@ -181,7 +184,8 @@ public final class RegistryEntries {
|
||||
entry(Registries.FROG_VARIANT, FrogVariants.class, Frog.Variant.class).writableApiRegistryBuilder(FrogVariantRegistryEntry.Builder.class, "PaperFrogVariantRegistryEntry.PaperBuilder").delayed(),
|
||||
entry(Registries.CHICKEN_VARIANT, ChickenVariants.class, Chicken.Variant.class).writableApiRegistryBuilder(ChickenVariantRegistryEntry.Builder.class, "PaperChickenVariantRegistryEntry.PaperBuilder"),
|
||||
entry(Registries.COW_VARIANT, CowVariants.class, Cow.Variant.class).writableApiRegistryBuilder(CowVariantRegistryEntry.Builder.class, "PaperCowVariantRegistryEntry.PaperBuilder"),
|
||||
entry(Registries.PIG_VARIANT, PigVariants.class, Pig.Variant.class).writableApiRegistryBuilder(PigVariantRegistryEntry.Builder.class, "PaperPigVariantRegistryEntry.PaperBuilder")
|
||||
entry(Registries.PIG_VARIANT, PigVariants.class, Pig.Variant.class).writableApiRegistryBuilder(PigVariantRegistryEntry.Builder.class, "PaperPigVariantRegistryEntry.PaperBuilder"),
|
||||
entry(Registries.DIALOG, Dialogs.class, Dialog.class, "Paper").allowDirect().writableApiRegistryBuilder(DialogRegistryEntry.Builder.class, "PaperDialogRegistryEntry.PaperBuilder")
|
||||
);
|
||||
|
||||
public static final List<RegistryEntry<?>> API_ONLY = List.of(
|
||||
|
@@ -23867,7 +23867,7 @@ index 46de98a6bbbae48c4837e1e588ba198a363d2dde..fd3553bdc1c3cdbf6aa3dc00e0a4987f
|
||||
thread1 -> {
|
||||
DedicatedServer dedicatedServer1 = new DedicatedServer(
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 75aba65cbe1a943f21c7464ff9465e64f63e8e5b..32475c0958fd7e0f1f9b494b0cc78a4a718d12b8 100644
|
||||
index 03a616fc9b0325aa163fe4950ec4ce9ffdd3a9ea..b6aee251027cf124d6597137abefc7fd177358c9 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -173,7 +173,7 @@ import net.minecraft.world.phys.Vec2;
|
||||
@@ -24059,7 +24059,7 @@ index 75aba65cbe1a943f21c7464ff9465e64f63e8e5b..32475c0958fd7e0f1f9b494b0cc78a4a
|
||||
return true;
|
||||
} else {
|
||||
boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
|
||||
@@ -2478,6 +2566,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -2479,6 +2567,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -48,10 +48,10 @@ index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdf
|
||||
+ }
|
||||
+}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index cff9d761dfee8a90b19fb2f3e678f99a39fc000c..0a260fdf6b198a8ab52e60bf6db2fb5eab719c48 100644
|
||||
index 41e24da7f0c120af96446603d234ddd417b0de60..7c5b6973769f6eda97f5a08a974b43a98f276ac7 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1718,6 +1718,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1719,6 +1719,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
|
||||
serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
|
||||
serverLevel.updateLagCompensationTick(); // Paper - lag compensation
|
||||
|
@@ -6,10 +6,10 @@ Subject: [PATCH] Optimise collision checking in player move packet handling
|
||||
Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision
|
||||
|
||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c8857c064d55 100644
|
||||
index 08aba415735733f5968fd032ab7ca249cdcf6cde..ee4397711625344622c81424afd11fd6d967efba 100644
|
||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -594,6 +594,7 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -606,6 +606,7 @@ public class ServerGamePacketListenerImpl
|
||||
}
|
||||
|
||||
rootVehicle.move(MoverType.PLAYER, new Vec3(d3, d4, d5));
|
||||
@@ -17,7 +17,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
|
||||
double verticalDelta = d4;
|
||||
d3 = d - rootVehicle.getX();
|
||||
d4 = d1 - rootVehicle.getY();
|
||||
@@ -605,12 +606,21 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -617,12 +618,21 @@ public class ServerGamePacketListenerImpl
|
||||
d7 = d3 * d3 + d4 * d4 + d5 * d5;
|
||||
boolean flag1 = false;
|
||||
if (d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
|
||||
@@ -42,7 +42,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
|
||||
rootVehicle.absSnapTo(x, y, z, f, f1);
|
||||
this.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle));
|
||||
rootVehicle.removeLatestMovementRecording();
|
||||
@@ -689,9 +699,32 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -701,9 +711,32 @@ public class ServerGamePacketListenerImpl
|
||||
}
|
||||
|
||||
private boolean noBlocksAround(Entity entity) {
|
||||
@@ -78,7 +78,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1467,7 +1500,7 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -1479,7 +1512,7 @@ public class ServerGamePacketListenerImpl
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
|
||||
d3 = d - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above
|
||||
d4 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above
|
||||
d5 = d2 - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above
|
||||
@@ -1506,6 +1539,7 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -1518,6 +1551,7 @@ public class ServerGamePacketListenerImpl
|
||||
boolean flag1 = this.player.verticalCollisionBelow;
|
||||
this.player.move(MoverType.PLAYER, new Vec3(d3, d4, d5));
|
||||
this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
|
||||
@@ -95,7 +95,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
|
||||
// Paper start - prevent position desync
|
||||
if (this.awaitingPositionFromClient != null) {
|
||||
return; // ... thanks Mojang for letting move calls teleport across dimensions.
|
||||
@@ -1538,7 +1572,17 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -1550,7 +1584,17 @@ public class ServerGamePacketListenerImpl
|
||||
}
|
||||
|
||||
// Paper start - Add fail move event
|
||||
@@ -114,7 +114,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
|
||||
if (!allowMovement) {
|
||||
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK,
|
||||
toX, toY, toZ, toYaw, toPitch, false);
|
||||
@@ -1674,7 +1718,7 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -1686,7 +1730,7 @@ public class ServerGamePacketListenerImpl
|
||||
|
||||
private boolean updateAwaitingTeleport() {
|
||||
if (this.awaitingPositionFromClient != null) {
|
||||
@@ -123,7 +123,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
|
||||
this.awaitingTeleportTime = this.tickCount;
|
||||
this.teleport(
|
||||
this.awaitingPositionFromClient.x,
|
||||
@@ -1693,6 +1737,33 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -1705,6 +1749,33 @@ public class ServerGamePacketListenerImpl
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -100,7 +100,7 @@ index 962084054c0208470d0c3c99c5dca6327c9b8752..2abc21102bbd2da79dc0c50826cff7da
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
index 140f0ac42626662354e9b5386d91f1b096a945a3..2e7c4c74818befd25e296b58ef9f20319544c2fb 100644
|
||||
index 1b42d5e9f9fd07f99009de6f4483648f416db733..08c59d603fca038fc2dde36384eea1b6c971e659 100644
|
||||
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
@@ -38,12 +38,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
@@ -137,7 +137,7 @@ index 140f0ac42626662354e9b5386d91f1b096a945a3..2e7c4c74818befd25e296b58ef9f2031
|
||||
// Paper end
|
||||
}
|
||||
|
||||
@@ -93,13 +95,41 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
@@ -99,13 +101,41 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
|
||||
@Override
|
||||
public void handleKeepAlive(ServerboundKeepAlivePacket packet) {
|
||||
@@ -185,7 +185,7 @@ index 140f0ac42626662354e9b5386d91f1b096a945a3..2e7c4c74818befd25e296b58ef9f2031
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -220,20 +250,23 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
@@ -232,20 +262,23 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
protected void keepConnectionAlive() {
|
||||
Profiler.get().push("keepAlive");
|
||||
long millis = Util.getMillis();
|
||||
@@ -223,7 +223,7 @@ index 140f0ac42626662354e9b5386d91f1b096a945a3..2e7c4c74818befd25e296b58ef9f2031
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,6 +446,6 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
@@ -425,6 +458,6 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
}
|
||||
|
||||
protected CommonListenerCookie createCookie(ClientInformation clientInformation) {
|
||||
|
@@ -20,7 +20,7 @@ index 5f2deeb5cc01d8bbeb7449bd4e59c466b3dfdf57..82824ae7ffbced513a8bcace684af949
|
||||
|
||||
@Override
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 0a260fdf6b198a8ab52e60bf6db2fb5eab719c48..52fa5112cd90ba766c94512a02401dd3aee82cc9 100644
|
||||
index 382d2b6b53bd144f4d56dccdc603ed0da8fe07a7..7aac2a6889af3edaebfaf94deecbf00d00758b68 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1654,33 +1654,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -63,9 +63,9 @@ index 0a260fdf6b198a8ab52e60bf6db2fb5eab719c48..52fa5112cd90ba766c94512a02401dd3
|
||||
- });
|
||||
- // Paper end - Folia scheduler API
|
||||
+ // Paper end - optimise Folia entity scheduler
|
||||
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper
|
||||
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
|
||||
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
|
||||
profilerFiller.push("commandFunctions");
|
||||
this.getFunctions().tick();
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index da880f52920b1101f23ef94f3fd0dbdea218c373..3d2c0a4d3a1f9d3e5cc6cd0cdb988ae1205de821 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
|
@@ -984,7 +984,7 @@
|
||||
ObjectArrayList<GameProfile> list = new ObjectArrayList<>(min);
|
||||
int randomInt = Mth.nextInt(this.random, 0, players.size() - min);
|
||||
|
||||
@@ -1040,17 +_,75 @@
|
||||
@@ -1040,17 +_,76 @@
|
||||
protected void tickChildren(BooleanSupplier hasTimeLeft) {
|
||||
ProfilerFiller profilerFiller = Profiler.get();
|
||||
this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing());
|
||||
@@ -1012,7 +1012,8 @@
|
||||
+ }
|
||||
+ });
|
||||
+ // Paper end - Folia scheduler API
|
||||
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper
|
||||
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
|
||||
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
|
||||
profilerFiller.push("commandFunctions");
|
||||
this.getFunctions().tick();
|
||||
profilerFiller.popPush("levels");
|
||||
|
@@ -0,0 +1,13 @@
|
||||
--- a/net/minecraft/server/dialog/body/ItemBody.java
|
||||
+++ b/net/minecraft/server/dialog/body/ItemBody.java
|
||||
@@ -15,8 +_,8 @@
|
||||
PlainMessage.CODEC.optionalFieldOf("description").forGetter(ItemBody::description),
|
||||
Codec.BOOL.optionalFieldOf("show_decorations", true).forGetter(ItemBody::showDecorations),
|
||||
Codec.BOOL.optionalFieldOf("show_tooltip", true).forGetter(ItemBody::showTooltip),
|
||||
- ExtraCodecs.intRange(1, 256).optionalFieldOf("width", 16).forGetter(ItemBody::width),
|
||||
- ExtraCodecs.intRange(1, 256).optionalFieldOf("height", 16).forGetter(ItemBody::height)
|
||||
+ ExtraCodecs.intRange(1, 256).optionalFieldOf("width", 16).forGetter(ItemBody::width), // Paper - diff on change - update builder defaults/limits
|
||||
+ ExtraCodecs.intRange(1, 256).optionalFieldOf("height", 16).forGetter(ItemBody::height) // Paper - diff on change - update builder defaults/limits
|
||||
)
|
||||
.apply(instance, ItemBody::new)
|
||||
);
|
@@ -27,7 +27,7 @@
|
||||
|
||||
public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) {
|
||||
this.server = server;
|
||||
@@ -52,6 +_,11 @@
|
||||
@@ -52,7 +_,18 @@
|
||||
this.keepAliveTime = Util.getMillis();
|
||||
this.latency = cookie.latency();
|
||||
this.transferred = cookie.transferred();
|
||||
@@ -37,8 +37,15 @@
|
||||
+ this.pluginMessagerChannels = cookie.channels();
|
||||
+ // Paper end
|
||||
}
|
||||
+
|
||||
+ // Paper start - configuration phase API
|
||||
+ public abstract io.papermc.paper.connection.PlayerCommonConnection getApiConnection();
|
||||
+
|
||||
+ public abstract net.kyori.adventure.audience.Audience getAudience();
|
||||
+ // Paper end - configuration phase API
|
||||
|
||||
private void close() {
|
||||
if (!this.closed) {
|
||||
@@ -82,7 +_,7 @@
|
||||
this.latency = (this.latency * 3 + i) / 4;
|
||||
this.keepAlivePending = false;
|
||||
@@ -48,7 +55,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +_,76 @@
|
||||
@@ -90,14 +_,88 @@
|
||||
public void handlePong(ServerboundPongPacket packet) {
|
||||
}
|
||||
|
||||
@@ -124,6 +131,18 @@
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCustomClickAction(ServerboundCustomClickActionPacket packet) {
|
||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.server);
|
||||
this.server.handleCustomClickAction(packet.id(), packet.payload());
|
||||
+ // Paper start - Implement click callbacks with custom click action
|
||||
+ final io.papermc.paper.event.player.PaperPlayerCustomClickEvent event = new io.papermc.paper.event.player.PaperPlayerCustomClickEvent(io.papermc.paper.adventure.PaperAdventure.asAdventure(packet.id()), this.getApiConnection(), packet.payload().orElse(null));
|
||||
+ event.callEvent();
|
||||
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.tryRunCallback(this.getAudience(), packet.id(), packet.payload());
|
||||
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.tryRunCallback(this.getAudience(), packet.id(), packet.payload());
|
||||
+ // Paper end - Implement click callbacks with custom click action
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,21 +_,46 @@
|
||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.server);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
|
||||
@@ -47,11 +_,13 @@
|
||||
@@ -47,12 +_,26 @@
|
||||
public ClientInformation clientInformation;
|
||||
@Nullable
|
||||
private SynchronizeRegistriesTask synchronizeRegistriesTask;
|
||||
@@ -10,10 +10,24 @@
|
||||
super(server, connection, cookie);
|
||||
this.gameProfile = cookie.gameProfile();
|
||||
this.clientInformation = cookie.clientInformation();
|
||||
- }
|
||||
+ this.paperConnection = new io.papermc.paper.connection.PaperPlayerConfigurationConnection(this); // Paper
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ // Paper start - configuration phase API
|
||||
+ @Override
|
||||
+ public io.papermc.paper.connection.PlayerCommonConnection getApiConnection() {
|
||||
+ return this.paperConnection;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public net.kyori.adventure.audience.Audience getAudience() {
|
||||
+ return this.paperConnection.getAudience();
|
||||
+ }
|
||||
+ // Paper end - configuration phase API
|
||||
|
||||
@Override
|
||||
protected GameProfile playerProfile() {
|
||||
@@ -61,6 +_,11 @@
|
||||
|
||||
@Override
|
||||
|
@@ -83,17 +83,31 @@
|
||||
|
||||
public ServerGamePacketListenerImpl(MinecraftServer server, Connection connection, ServerPlayer player, CommonListenerCookie cookie) {
|
||||
super(server, connection, cookie);
|
||||
@@ -268,7 +_,9 @@
|
||||
@@ -268,8 +_,22 @@
|
||||
player.connection = this;
|
||||
player.getTextFilter().join();
|
||||
this.signedMessageDecoder = SignedMessageChain.Decoder.unsigned(player.getUUID(), server::enforceSecureProfile);
|
||||
- this.chatMessageChain = new FutureChain(server);
|
||||
- }
|
||||
+ this.chatMessageChain = new FutureChain(server.chatExecutor); // CraftBukkit - async chat
|
||||
+ this.tickEndEvent = new io.papermc.paper.event.packet.ClientTickEndEvent(player.getBukkitEntity()); // Paper - add client tick end event
|
||||
+ this.playerGameConnection = new io.papermc.paper.connection.PaperPlayerGameConnection(this); // Paper
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ // Paper start - configuration phase API
|
||||
+ @Override
|
||||
+ public io.papermc.paper.connection.PlayerCommonConnection getApiConnection() {
|
||||
+ return this.playerGameConnection;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public net.kyori.adventure.audience.Audience getAudience() {
|
||||
+ return this.getCraftPlayer();
|
||||
+ }
|
||||
+ // Paper end - configuration phase API
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
@@ -288,8 +_,8 @@
|
||||
this.knownMovePacketCount = this.receivedMovePacketCount;
|
||||
if (this.clientIsFloating && !this.player.isSleeping() && !this.player.isPassenger() && !this.player.isDeadOrDying()) {
|
||||
@@ -2610,7 +2624,7 @@
|
||||
if (!this.receivedMovementThisTick) {
|
||||
this.player.setKnownMovement(Vec3.ZERO);
|
||||
}
|
||||
@@ -2078,4 +_,92 @@
|
||||
@@ -2078,4 +_,80 @@
|
||||
interface EntityInteraction {
|
||||
InteractionResult run(ServerPlayer player, Entity entity, InteractionHand hand);
|
||||
}
|
||||
@@ -2628,18 +2642,6 @@
|
||||
+ }
|
||||
+ // Paper end - Add fail move event
|
||||
+
|
||||
+ // Paper start - Implement click callbacks with custom click action
|
||||
+ @Override
|
||||
+ public void handleCustomClickAction(final net.minecraft.network.protocol.common.ServerboundCustomClickActionPacket packet) {
|
||||
+ super.handleCustomClickAction(packet);
|
||||
+ if (packet.id().equals(io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CLICK_CALLBACK_RESOURCE_LOCATION)) {
|
||||
+ packet.payload().ifPresent(tag ->
|
||||
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.tryRunCallback(this.player.getBukkitEntity(), tag)
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Implement click callbacks with custom click action
|
||||
+
|
||||
+ // Paper start - add utilities
|
||||
+ public org.bukkit.craftbukkit.entity.CraftPlayer getCraftPlayer() {
|
||||
+ return this.player == null ? null : this.player.getBukkitEntity();
|
||||
|
@@ -5,6 +5,9 @@ import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.registry.data.dialog.PaperDialogCodecs;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -13,7 +16,7 @@ import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import java.util.function.Supplier;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.nbt.api.BinaryTagHolder;
|
||||
import net.kyori.adventure.text.BlockNBTComponent;
|
||||
@@ -36,12 +39,12 @@ import net.kyori.adventure.text.format.ShadowColor;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.util.Index;
|
||||
import net.minecraft.commands.arguments.selector.SelectorPattern;
|
||||
import net.minecraft.core.UUIDUtil;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.nbt.TagParser;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.chat.ComponentSerialization;
|
||||
import net.minecraft.network.chat.contents.KeybindContents;
|
||||
@@ -49,7 +52,6 @@ import net.minecraft.network.chat.contents.ScoreContents;
|
||||
import net.minecraft.network.chat.contents.TranslatableContents;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.ExtraCodecs;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.item.Item;
|
||||
@@ -98,7 +100,7 @@ public final class AdventureCodecs {
|
||||
}
|
||||
});
|
||||
|
||||
static final Codec<Key> KEY_CODEC = Codec.STRING.comapFlatMap(s -> {
|
||||
public static final Codec<Key> KEY_CODEC = Codec.STRING.comapFlatMap(s -> {
|
||||
return Key.parseable(s) ? DataResult.success(Key.key(s)) : DataResult.error(() -> "Cannot convert " + s + " to adventure Key");
|
||||
}, Key::asString);
|
||||
|
||||
@@ -129,6 +131,10 @@ public final class AdventureCodecs {
|
||||
static final MapCodec<ClickEvent> COPY_TO_CLIPBOARD_CODEC = mapCodec((instance) -> instance.group(
|
||||
Codec.STRING.fieldOf("value").forGetter(TEXT_PAYLOAD_EXTRACTOR)
|
||||
).apply(instance, ClickEvent::copyToClipboard));
|
||||
// needs to be lazy loaded due to depending on PaperDialogCodecs static init
|
||||
static final MapCodec<ClickEvent> SHOW_DIALOG_CODEC = MapCodec.recursive("show_dialog", ignored -> mapCodec((instance) -> instance.group(
|
||||
PaperDialogCodecs.DIALOG_CODEC.fieldOf("dialog").forGetter(a -> (Dialog) ((ClickEvent.Payload.Dialog) a.payload()).dialog())
|
||||
).apply(instance, ClickEvent::showDialog)));
|
||||
static final MapCodec<ClickEvent> CUSTOM_CODEC = mapCodec((instance) -> instance.group(
|
||||
KEY_CODEC.fieldOf("id").forGetter(a -> ((ClickEvent.Payload.Custom) a.payload()).key()),
|
||||
BINARY_TAG_HOLDER_CODEC.fieldOf("payload").forGetter(a -> ((ClickEvent.Payload.Custom) a.payload()).nbt())
|
||||
@@ -140,17 +146,19 @@ public final class AdventureCodecs {
|
||||
static final ClickEventType SUGGEST_COMMAND_CLICK_EVENT_TYPE = new ClickEventType(SUGGEST_COMMAND_CODEC, "suggest_command");
|
||||
static final ClickEventType CHANGE_PAGE_CLICK_EVENT_TYPE = new ClickEventType(CHANGE_PAGE_CODEC, "change_page");
|
||||
static final ClickEventType COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE = new ClickEventType(COPY_TO_CLIPBOARD_CODEC, "copy_to_clipboard");
|
||||
static final ClickEventType SHOW_DIALOG_CLICK_EVENT_TYPE = new ClickEventType(SHOW_DIALOG_CODEC, "show_dialog");
|
||||
static final ClickEventType CUSTOM_CLICK_EVENT_TYPE = new ClickEventType(CUSTOM_CODEC, "custom");
|
||||
static final Codec<ClickEventType> CLICK_EVENT_TYPE_CODEC = StringRepresentable.fromValues(() -> new ClickEventType[]{OPEN_URL_CLICK_EVENT_TYPE, OPEN_FILE_CLICK_EVENT_TYPE, RUN_COMMAND_CLICK_EVENT_TYPE, SUGGEST_COMMAND_CLICK_EVENT_TYPE, CHANGE_PAGE_CLICK_EVENT_TYPE, COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE, CUSTOM_CLICK_EVENT_TYPE});
|
||||
public static final Supplier<ClickEventType[]> CLICK_EVENT_TYPES = () -> new ClickEventType[]{OPEN_URL_CLICK_EVENT_TYPE, OPEN_FILE_CLICK_EVENT_TYPE, RUN_COMMAND_CLICK_EVENT_TYPE, SUGGEST_COMMAND_CLICK_EVENT_TYPE, CHANGE_PAGE_CLICK_EVENT_TYPE, COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE, SHOW_DIALOG_CLICK_EVENT_TYPE, CUSTOM_CLICK_EVENT_TYPE};
|
||||
static final Codec<ClickEventType> CLICK_EVENT_TYPE_CODEC = StringRepresentable.fromValues(CLICK_EVENT_TYPES);
|
||||
|
||||
record ClickEventType(MapCodec<ClickEvent> codec, String id) implements StringRepresentable {
|
||||
public record ClickEventType(MapCodec<ClickEvent> codec, String id) implements StringRepresentable {
|
||||
@Override
|
||||
public String getSerializedName() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Function<ClickEvent, ClickEventType> GET_CLICK_EVENT_TYPE =
|
||||
public static final Function<ClickEvent, ClickEventType> GET_CLICK_EVENT_TYPE =
|
||||
he -> switch (he.action()) {
|
||||
case OPEN_URL -> OPEN_URL_CLICK_EVENT_TYPE;
|
||||
case OPEN_FILE -> OPEN_FILE_CLICK_EVENT_TYPE;
|
||||
@@ -158,7 +166,7 @@ public final class AdventureCodecs {
|
||||
case SUGGEST_COMMAND -> SUGGEST_COMMAND_CLICK_EVENT_TYPE;
|
||||
case CHANGE_PAGE -> CHANGE_PAGE_CLICK_EVENT_TYPE;
|
||||
case COPY_TO_CLIPBOARD -> COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE;
|
||||
case SHOW_DIALOG -> throw new UnsupportedOperationException(); // todo: dialog codec with dialog "api"
|
||||
case SHOW_DIALOG -> SHOW_DIALOG_CLICK_EVENT_TYPE;
|
||||
case CUSTOM -> CUSTOM_CLICK_EVENT_TYPE;
|
||||
};
|
||||
|
||||
@@ -450,6 +458,23 @@ public final class AdventureCodecs {
|
||||
return component;
|
||||
}
|
||||
|
||||
public static final Codec<BinaryTagHolder> BINARY_TAG_HOLDER_COMPOUND_CODEC = CompoundTag.CODEC.flatComapMap(PaperAdventure::asBinaryTagHolder, api -> {
|
||||
try {
|
||||
final Tag tag = api.get(PaperAdventure.NBT_CODEC);
|
||||
if (!(tag instanceof final CompoundTag compoundTag)) {
|
||||
return DataResult.error(() -> "Expected a CompoundTag, but got " + tag.getClass().getSimpleName());
|
||||
}
|
||||
return DataResult.success(compoundTag);
|
||||
} catch (CommandSyntaxException e) {
|
||||
return DataResult.error(e::getMessage);
|
||||
}
|
||||
});
|
||||
|
||||
public static <T> Codec<T> indexCodec(final Index<String, T> index) {
|
||||
return Codec.of(Codec.STRING.comap(index::keyOrThrow), Codec.STRING.map(index::valueOrThrow));
|
||||
|
||||
}
|
||||
|
||||
private AdventureCodecs() {
|
||||
}
|
||||
}
|
||||
|
@@ -1,15 +1,22 @@
|
||||
package io.papermc.paper.adventure.providers;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import io.papermc.paper.dialog.PaperDialogResponseView;
|
||||
import io.papermc.paper.registry.data.dialog.action.DialogActionCallback;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.event.ClickCallback;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.minecraft.core.UUIDUtil;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -17,30 +24,81 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage") // permitted provider
|
||||
public class ClickCallbackProviderImpl implements ClickCallback.Provider {
|
||||
private static final Key CLICK_CALLBACK_KEY = Key.key("paper", "click_callback");
|
||||
private static final String ID_KEY = "id";
|
||||
private static final Key ADVENTURE_CLICK_CALLBACK_KEY = Key.key("paper", "click_callback");
|
||||
public static final Key DIALOG_CLICK_CALLBACK_KEY = Key.key("paper", "dialog_click_callback");
|
||||
public static final String ID_KEY = "id";
|
||||
|
||||
public static final ResourceLocation CLICK_CALLBACK_RESOURCE_LOCATION = PaperAdventure.asVanilla(CLICK_CALLBACK_KEY);
|
||||
public static final CallbackManager CALLBACK_MANAGER = new CallbackManager();
|
||||
public static final AdventureClick ADVENTURE_CLICK_MANAGER = new AdventureClick();
|
||||
public static final DialogClickManager DIALOG_CLICK_MANAGER = new DialogClickManager();
|
||||
|
||||
@Override
|
||||
public @NotNull ClickEvent create(final @NotNull ClickCallback<Audience> callback, final ClickCallback.@NotNull Options options) {
|
||||
final CompoundTag tag = new CompoundTag();
|
||||
tag.putString(ID_KEY, CALLBACK_MANAGER.addCallback(callback, options).toString());
|
||||
return ClickEvent.custom(CLICK_CALLBACK_KEY, PaperAdventure.asBinaryTagHolder(tag));
|
||||
tag.store(ID_KEY, UUIDUtil.CODEC, ADVENTURE_CLICK_MANAGER.addCallback(callback, options));
|
||||
return ClickEvent.custom(ADVENTURE_CLICK_CALLBACK_KEY, PaperAdventure.asBinaryTagHolder(tag));
|
||||
}
|
||||
|
||||
public static final class CallbackManager {
|
||||
public static final class AdventureClick extends CallbackManager<ClickCallback<Audience>, UUID> {
|
||||
|
||||
private final Map<UUID, StoredCallback> callbacks = new HashMap<>();
|
||||
private final Queue<StoredCallback> queue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private CallbackManager() {
|
||||
private AdventureClick() {
|
||||
super(PaperAdventure.asVanilla(ADVENTURE_CLICK_CALLBACK_KEY)::equals);
|
||||
}
|
||||
|
||||
public UUID addCallback(final @NotNull ClickCallback<Audience> callback, final ClickCallback.@NotNull Options options) {
|
||||
final UUID id = UUID.randomUUID();
|
||||
this.queue.add(new StoredCallback(callback, options, id));
|
||||
return this.addCallback(UUID.randomUUID(), callback, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
void doRunCallback(final @NotNull Audience audience, final Key key, final Tag tag) {
|
||||
tag.asCompound().ifPresent(t -> {
|
||||
final Optional<UUID> id = t.read(ID_KEY, UUIDUtil.CODEC);
|
||||
if (id.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this.tryConsumeCallback(id.get(), callback -> {
|
||||
callback.accept(audience);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static final class DialogClickManager extends CallbackManager<DialogActionCallback, UUID> {
|
||||
|
||||
public DialogClickManager() {
|
||||
super(PaperAdventure.asVanilla(DIALOG_CLICK_CALLBACK_KEY)::equals);
|
||||
}
|
||||
|
||||
@Override
|
||||
void doRunCallback(final @NotNull Audience audience, final Key key, final Tag tag) {
|
||||
tag.asCompound().ifPresent(t -> {
|
||||
final Optional<UUID> id = t.read(ID_KEY, UUIDUtil.CODEC);
|
||||
if (id.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this.tryConsumeCallback(id.get(), callback -> {
|
||||
callback.accept(PaperDialogResponseView.createUnvalidatedResponse(t), audience);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract static class CallbackManager<C, I> {
|
||||
|
||||
private final Predicate<ResourceLocation> locationPredicate;
|
||||
protected final Map<I, StoredCallback<C, I>> callbacks = new HashMap<>();
|
||||
private final Queue<StoredCallback<C, I>> queue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
protected CallbackManager(final Predicate<ResourceLocation> locationPredicate) {
|
||||
this.locationPredicate = locationPredicate;
|
||||
}
|
||||
|
||||
protected CallbackManager() {
|
||||
this.locationPredicate = Predicates.alwaysTrue();
|
||||
}
|
||||
|
||||
public I addCallback(final I id, final @NotNull C callback, final ClickCallback.@NotNull Options options) {
|
||||
this.queue.add(new StoredCallback<>(callback, options, id));
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -51,38 +109,36 @@ public class ClickCallbackProviderImpl implements ClickCallback.Provider {
|
||||
}
|
||||
|
||||
// Add entries from queue
|
||||
StoredCallback callback;
|
||||
StoredCallback<C, I> callback;
|
||||
while ((callback = this.queue.poll()) != null) {
|
||||
this.callbacks.put(callback.id(), callback);
|
||||
}
|
||||
}
|
||||
|
||||
public void tryRunCallback(final @NotNull Audience audience, final Tag tag) {
|
||||
tag.asCompound().flatMap(t -> t.getString(ID_KEY)).ifPresent(s -> {
|
||||
final UUID id;
|
||||
try {
|
||||
id = UUID.fromString(s);
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
return;
|
||||
}
|
||||
final void tryConsumeCallback(final I key, final Consumer<? super C> callbackConsumer) {
|
||||
final StoredCallback<C, I> callback = this.callbacks.get(key);
|
||||
if (callback != null && callback.valid()) {
|
||||
callback.takeUse();
|
||||
callbackConsumer.accept(callback.callback);
|
||||
}
|
||||
}
|
||||
|
||||
final StoredCallback callback = this.callbacks.get(id);
|
||||
if (callback != null && callback.valid()) {
|
||||
callback.takeUse();
|
||||
callback.callback.accept(audience);
|
||||
}
|
||||
});
|
||||
abstract void doRunCallback(final @NotNull Audience audience, final Key key, final Tag tag);
|
||||
|
||||
public final void tryRunCallback(final @NotNull Audience audience, final ResourceLocation key, final Optional<? extends Tag> tag) {
|
||||
if (!this.locationPredicate.test(key) || tag.isEmpty()) return;
|
||||
this.doRunCallback(audience, PaperAdventure.asAdventure(key), tag.get());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class StoredCallback {
|
||||
public static final class StoredCallback<C, I> {
|
||||
private final long startedAt = System.nanoTime();
|
||||
private final ClickCallback<Audience> callback;
|
||||
private final C callback;
|
||||
private final long lifetime;
|
||||
private final UUID id;
|
||||
private final I id;
|
||||
private int remainingUses;
|
||||
|
||||
private StoredCallback(final @NotNull ClickCallback<Audience> callback, final ClickCallback.@NotNull Options options, final UUID id) {
|
||||
private StoredCallback(final @NotNull C callback, final ClickCallback.@NotNull Options options, final I id) {
|
||||
long lifetimeValue;
|
||||
this.callback = callback;
|
||||
try {
|
||||
@@ -95,7 +151,7 @@ public class ClickCallbackProviderImpl implements ClickCallback.Provider {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void takeUse() {
|
||||
private void takeUse() {
|
||||
if (this.remainingUses != ClickCallback.UNLIMITED_USES) {
|
||||
this.remainingUses--;
|
||||
}
|
||||
@@ -110,11 +166,11 @@ public class ClickCallbackProviderImpl implements ClickCallback.Provider {
|
||||
return System.nanoTime() - this.startedAt >= this.lifetime;
|
||||
}
|
||||
|
||||
public boolean valid() {
|
||||
return hasRemainingUses() && !expired();
|
||||
private boolean valid() {
|
||||
return this.hasRemainingUses() && !this.expired();
|
||||
}
|
||||
|
||||
public UUID id() {
|
||||
public I id() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
|
@@ -3,26 +3,31 @@ package io.papermc.paper.connection;
|
||||
import com.destroystokyo.paper.profile.CraftPlayerProfile;
|
||||
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.dialog.PaperDialog;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.dialog.DialogLike;
|
||||
import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.pointer.Pointers;
|
||||
import net.kyori.adventure.resource.ResourcePackCallback;
|
||||
import net.kyori.adventure.resource.ResourcePackInfo;
|
||||
import net.kyori.adventure.resource.ResourcePackRequest;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.common.ClientboundResourcePackPopPacket;
|
||||
import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket;
|
||||
import net.minecraft.network.protocol.common.ClientboundShowDialogPacket;
|
||||
import net.minecraft.network.protocol.configuration.ClientboundResetChatPacket;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.network.ConfigurationTask;
|
||||
import net.minecraft.server.network.ServerConfigurationPacketListenerImpl;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PaperPlayerConfigurationConnection extends PaperCommonConnection<ServerConfigurationPacketListenerImpl> implements PlayerConfigurationConnection, Audience, PluginMessageBridgeImpl {
|
||||
|
||||
private @Nullable Pointers adventurePointers;
|
||||
@@ -37,13 +42,13 @@ public class PaperPlayerConfigurationConnection extends PaperCommonConnection<Se
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendResourcePacks(ResourcePackRequest request) {
|
||||
final List<ClientboundResourcePackPushPacket> packs = new java.util.ArrayList<>(request.packs().size());
|
||||
public void sendResourcePacks(final ResourcePackRequest request) {
|
||||
final List<ClientboundResourcePackPushPacket> packs = new ArrayList<>(request.packs().size());
|
||||
if (request.replace()) {
|
||||
this.clearResourcePacks();
|
||||
}
|
||||
final net.minecraft.network.chat.Component prompt = PaperAdventure.asVanilla(request.prompt());
|
||||
for (final java.util.Iterator<ResourcePackInfo> iter = request.packs().iterator(); iter.hasNext(); ) {
|
||||
final Component prompt = PaperAdventure.asVanilla(request.prompt());
|
||||
for (final Iterator<ResourcePackInfo> iter = request.packs().iterator(); iter.hasNext(); ) {
|
||||
final ResourcePackInfo pack = iter.next();
|
||||
packs.add(new ClientboundResourcePackPushPacket(pack.id(), pack.uri().toASCIIString(), pack.hash(), request.required(), iter.hasNext() ? Optional.empty() : Optional.ofNullable(prompt)));
|
||||
if (request.callback() != ResourcePackCallback.noOp()) {
|
||||
@@ -54,7 +59,7 @@ public class PaperPlayerConfigurationConnection extends PaperCommonConnection<Se
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResourcePacks(UUID id, UUID... others) {
|
||||
public void removeResourcePacks(final UUID id, final UUID... others) {
|
||||
net.kyori.adventure.util.MonkeyBars.nonEmptyArrayToList(pack -> new ClientboundResourcePackPopPacket(Optional.of(pack)), id, others).forEach(this.handle::send);
|
||||
}
|
||||
|
||||
@@ -63,13 +68,18 @@ public class PaperPlayerConfigurationConnection extends PaperCommonConnection<Se
|
||||
this.handle.send(new ClientboundResourcePackPopPacket(Optional.empty()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showDialog(final DialogLike dialog) {
|
||||
this.handle.send(new ClientboundShowDialogPacket(PaperDialog.bukkitToMinecraftHolder((Dialog) dialog)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pointers pointers() {
|
||||
if (this.adventurePointers == null) {
|
||||
this.adventurePointers = Pointers.builder()
|
||||
.withDynamic(Identity.NAME, () -> this.handle.getOwner().getName())
|
||||
.withDynamic(Identity.UUID, () -> this.handle.getOwner().getId())
|
||||
.build();
|
||||
.withDynamic(Identity.NAME, () -> this.handle.getOwner().getName())
|
||||
.withDynamic(Identity.UUID, () -> this.handle.getOwner().getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
return this.adventurePointers;
|
||||
@@ -92,7 +102,7 @@ public class PaperPlayerConfigurationConnection extends PaperCommonConnection<Se
|
||||
|
||||
@Override
|
||||
public void completeReconfiguration() {
|
||||
ConfigurationTask task = this.handle.currentTask;
|
||||
final ConfigurationTask task = this.handle.currentTask;
|
||||
if (task != null) {
|
||||
// This means that the player is going through the normal configuration process, or is already returning to the game phase.
|
||||
// Be safe and just ignore, as many plugins may call this.
|
||||
|
@@ -0,0 +1,29 @@
|
||||
package io.papermc.paper.dialog;
|
||||
|
||||
import io.papermc.paper.registry.HolderableBase;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import org.bukkit.craftbukkit.CraftRegistry;
|
||||
|
||||
public final class PaperDialog extends HolderableBase<net.minecraft.server.dialog.Dialog> implements Dialog {
|
||||
|
||||
public static net.minecraft.server.dialog.Dialog bukkitToMinecraft(final Dialog bukkit) {
|
||||
return CraftRegistry.bukkitToMinecraft(bukkit);
|
||||
}
|
||||
|
||||
public static Holder<net.minecraft.server.dialog.Dialog> bukkitToMinecraftHolder(final Dialog bukkit) {
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
|
||||
}
|
||||
|
||||
public static Dialog minecraftToBukkit(final net.minecraft.server.dialog.Dialog minecraft) {
|
||||
return CraftRegistry.minecraftToBukkit(minecraft, Registries.DIALOG);
|
||||
}
|
||||
|
||||
public static Dialog minecraftHolderToBukkit(final Holder<net.minecraft.server.dialog.Dialog> minecraft) {
|
||||
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.DIALOG);
|
||||
}
|
||||
|
||||
public PaperDialog(final Holder<net.minecraft.server.dialog.Dialog> holder) {
|
||||
super(holder);
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
package io.papermc.paper.dialog;
|
||||
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import net.kyori.adventure.nbt.api.BinaryTagHolder;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class PaperDialogResponseView implements DialogResponseView {
|
||||
|
||||
private final CompoundTag payload;
|
||||
|
||||
private PaperDialogResponseView(final CompoundTag payload) {
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public static DialogResponseView createUnvalidatedResponse(final CompoundTag tag) {
|
||||
return new PaperDialogResponseView(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryTagHolder payload() {
|
||||
return BinaryTagHolder.encode(this.payload, PaperAdventure.NBT_CODEC);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getText(final String key) {
|
||||
if (!this.payload.contains(key)) {
|
||||
return null;
|
||||
}
|
||||
return this.payload.getString(key).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Boolean getBoolean(final String key) {
|
||||
if (!this.payload.contains(key)) {
|
||||
return null;
|
||||
}
|
||||
return this.payload.getBoolean(key).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Float getFloat(final String key) {
|
||||
if (!this.payload.contains(key)) {
|
||||
return null;
|
||||
}
|
||||
return this.payload.getFloat(key).orElse(null);
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package io.papermc.paper.event.player;
|
||||
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import io.papermc.paper.connection.PlayerCommonConnection;
|
||||
import io.papermc.paper.dialog.DialogResponseView;
|
||||
import io.papermc.paper.dialog.PaperDialogResponseView;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.nbt.api.BinaryTagHolder;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class PaperPlayerCustomClickEvent extends PlayerCustomClickEvent {
|
||||
|
||||
private final @Nullable Tag payload;
|
||||
private @Nullable BinaryTagHolder apiPayload;
|
||||
|
||||
private @Nullable DialogResponseView rawResponse;
|
||||
|
||||
public PaperPlayerCustomClickEvent(final Key key, final PlayerCommonConnection commonConnection, final @Nullable Tag payload) {
|
||||
super(key, commonConnection);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BinaryTagHolder getTag() {
|
||||
if (this.apiPayload == null && this.payload != null) {
|
||||
this.apiPayload = BinaryTagHolder.encode(this.payload, PaperAdventure.NBT_CODEC);
|
||||
}
|
||||
return this.apiPayload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DialogResponseView getDialogResponseView() {
|
||||
if (this.payload == null || !(this.payload instanceof final CompoundTag compoundPayload)) {
|
||||
return null;
|
||||
}
|
||||
if (this.rawResponse == null) {
|
||||
this.rawResponse = PaperDialogResponseView.createUnvalidatedResponse(compoundPayload);
|
||||
}
|
||||
return this.rawResponse;
|
||||
}
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package io.papermc.paper.event.player;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
@@ -4,6 +4,8 @@ import com.google.common.base.Preconditions;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import io.papermc.paper.datacomponent.DataComponentTypes;
|
||||
import io.papermc.paper.datacomponent.PaperDataComponentType;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.dialog.PaperDialog;
|
||||
import io.papermc.paper.registry.data.PaperBannerPatternRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperCatTypeRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperChickenVariantRegistryEntry;
|
||||
@@ -18,6 +20,7 @@ import io.papermc.paper.registry.data.PaperPaintingVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperPigVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperSoundEventRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperWolfVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.dialog.PaperDialogRegistryEntry;
|
||||
import io.papermc.paper.registry.entry.RegistryEntry;
|
||||
import io.papermc.paper.registry.entry.RegistryEntryMeta;
|
||||
import io.papermc.paper.registry.tag.TagKey;
|
||||
@@ -132,6 +135,7 @@ public final class PaperRegistries {
|
||||
start(Registries.CHICKEN_VARIANT, RegistryKey.CHICKEN_VARIANT).craft(Chicken.Variant.class, CraftChicken.CraftVariant::new).writable(PaperChickenVariantRegistryEntry.PaperBuilder::new),
|
||||
start(Registries.COW_VARIANT, RegistryKey.COW_VARIANT).craft(Cow.Variant.class, CraftCow.CraftVariant::new).writable(PaperCowVariantRegistryEntry.PaperBuilder::new),
|
||||
start(Registries.PIG_VARIANT, RegistryKey.PIG_VARIANT).craft(Pig.Variant.class, CraftPig.CraftVariant::new).writable(PaperPigVariantRegistryEntry.PaperBuilder::new),
|
||||
start(Registries.DIALOG, RegistryKey.DIALOG).craft(Dialog.class, PaperDialog::new, true).writable(PaperDialogRegistryEntry.PaperBuilder::new),
|
||||
|
||||
// api-only
|
||||
start(Registries.ENTITY_TYPE, RegistryKey.ENTITY_TYPE).apiOnly(PaperSimpleRegistry::entityType),
|
||||
@@ -161,13 +165,13 @@ public final class PaperRegistries {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntryMeta.Buildable<M, T, B> getBuildableMeta(final ResourceKey<? extends Registry<M>> resourceKey) {
|
||||
final RegistryEntry<M, T> entry = getEntry(resourceKey);
|
||||
public static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntryMeta.Buildable<M, T, B> getBuildableMeta(final RegistryKey<T> registryKey) {
|
||||
final RegistryEntry<M, T> entry = getEntry(registryKey);
|
||||
if (entry == null) {
|
||||
throw new IllegalArgumentException("No registry entry for " + resourceKey);
|
||||
throw new IllegalArgumentException("No registry entry for " + registryKey);
|
||||
}
|
||||
if (!(entry.meta() instanceof final RegistryEntryMeta.Buildable<M, T, ?> buildableMeta)) {
|
||||
throw new IllegalArgumentException("Registry entry for " + resourceKey + " is not buildable");
|
||||
throw new IllegalArgumentException("Registry entry for " + registryKey + " is not buildable");
|
||||
}
|
||||
return (RegistryEntryMeta.Buildable<M, T, B>) buildableMeta;
|
||||
}
|
||||
|
@@ -1,15 +1,22 @@
|
||||
package io.papermc.paper.registry.data;
|
||||
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.registry.RegistryBuilderFactory;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
|
||||
import io.papermc.paper.registry.data.util.Conversions;
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import org.bukkit.MusicInstrument;
|
||||
|
||||
public final class InlinedRegistryBuilderProviderImpl implements InlinedRegistryBuilderProvider {
|
||||
|
||||
@Override
|
||||
public MusicInstrument createInstrument(final Consumer<RegistryBuilderFactory<MusicInstrument, ? extends InstrumentRegistryEntry.Builder>> value) {
|
||||
return Conversions.global().createApiInstanceFromBuilder(Registries.INSTRUMENT, value);
|
||||
return Conversions.global().createApiInstanceFromBuilder(RegistryKey.INSTRUMENT, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog createDialog(final Consumer<RegistryBuilderFactory<Dialog, ? extends DialogRegistryEntry.Builder>> value) {
|
||||
return Conversions.global().createApiInstanceFromBuilder(RegistryKey.DIALOG, value);
|
||||
}
|
||||
}
|
||||
|
@@ -3,13 +3,13 @@ package io.papermc.paper.registry.data;
|
||||
import io.papermc.paper.registry.PaperRegistries;
|
||||
import io.papermc.paper.registry.PaperRegistryBuilder;
|
||||
import io.papermc.paper.registry.RegistryBuilderFactory;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import io.papermc.paper.registry.data.util.Conversions;
|
||||
import io.papermc.paper.registry.holder.PaperRegistryHolders;
|
||||
import io.papermc.paper.registry.holder.RegistryHolder;
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.world.item.Instrument;
|
||||
@@ -77,7 +77,7 @@ public class PaperInstrumentRegistryEntry implements InstrumentRegistryEntry {
|
||||
|
||||
@Override
|
||||
public Builder soundEvent(final Consumer<RegistryBuilderFactory<Sound, ? extends SoundEventRegistryEntry.Builder>> soundEvent) {
|
||||
this.soundEvent = this.conversions.createHolderFromBuilder(Registries.SOUND_EVENT, asArgument(soundEvent, "soundEvent"));
|
||||
this.soundEvent = this.conversions.createHolderFromBuilder(RegistryKey.SOUND_EVENT, asArgument(soundEvent, "soundEvent"));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,7 @@ package io.papermc.paper.registry.data;
|
||||
import io.papermc.paper.registry.PaperRegistries;
|
||||
import io.papermc.paper.registry.PaperRegistryBuilder;
|
||||
import io.papermc.paper.registry.RegistryBuilderFactory;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import io.papermc.paper.registry.data.util.Conversions;
|
||||
import io.papermc.paper.registry.holder.PaperRegistryHolders;
|
||||
@@ -10,7 +11,6 @@ import io.papermc.paper.registry.holder.RegistryHolder;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.world.item.JukeboxSong;
|
||||
@@ -80,7 +80,7 @@ public class PaperJukeboxSongRegistryEntry implements JukeboxSongRegistryEntry {
|
||||
|
||||
@Override
|
||||
public JukeboxSongRegistryEntry.Builder soundEvent(final Consumer<RegistryBuilderFactory<Sound, ? extends SoundEventRegistryEntry.Builder>> soundEvent) {
|
||||
this.soundEvent = this.conversions.createHolderFromBuilder(Registries.SOUND_EVENT, asArgument(soundEvent, "soundEvent"));
|
||||
this.soundEvent = this.conversions.createHolderFromBuilder(RegistryKey.SOUND_EVENT, asArgument(soundEvent, "soundEvent"));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,46 @@
|
||||
package io.papermc.paper.registry.data.dialog;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.papermc.paper.registry.data.dialog.action.DialogAction;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.server.dialog.CommonButtonData;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public record ActionButtonImpl(Component label, @Nullable Component tooltip, int width, @Nullable DialogAction action) implements ActionButton {
|
||||
|
||||
public static final class BuilderImpl implements ActionButton.Builder {
|
||||
|
||||
private final Component label;
|
||||
private @Nullable Component tooltip;
|
||||
private int width = CommonButtonData.DEFAULT_WIDTH;
|
||||
private @Nullable DialogAction action;
|
||||
|
||||
public BuilderImpl(final Component label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder tooltip(final @Nullable Component tooltip) {
|
||||
this.tooltip = tooltip;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder width(final int width) {
|
||||
Preconditions.checkArgument(width >= 1 && width <= 1024, "Width must be between 1 and 1024");
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder action(final @Nullable DialogAction action) {
|
||||
this.action = action;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionButton build() {
|
||||
return new ActionButtonImpl(this.label, this.tooltip, this.width, this.action);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
package io.papermc.paper.registry.data.dialog;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.papermc.paper.registry.data.dialog.body.DialogBody;
|
||||
import io.papermc.paper.registry.data.dialog.input.DialogInput;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public record DialogBaseImpl(
|
||||
Component title,
|
||||
@Nullable Component externalTitle,
|
||||
boolean canCloseWithEscape,
|
||||
boolean pause,
|
||||
DialogAfterAction afterAction,
|
||||
@Unmodifiable List<DialogBody> body,
|
||||
@Unmodifiable List<DialogInput> inputs
|
||||
) implements DialogBase {
|
||||
|
||||
public DialogBaseImpl {
|
||||
Preconditions.checkArgument(!pause || afterAction != DialogAfterAction.NONE);
|
||||
body = List.copyOf(body);
|
||||
inputs = List.copyOf(inputs);
|
||||
}
|
||||
|
||||
public static final class BuilderImpl implements DialogBase.Builder {
|
||||
|
||||
private final Component title;
|
||||
private @Nullable Component externalTitle;
|
||||
private boolean canCloseWithEscape = true;
|
||||
private boolean pause = true;
|
||||
private DialogAfterAction afterAction = DialogAfterAction.CLOSE;
|
||||
private List<DialogBody> body = Collections.emptyList();
|
||||
private List<DialogInput> inputs = Collections.emptyList();
|
||||
|
||||
public BuilderImpl(final Component title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl externalTitle(final @Nullable Component externalTitle) {
|
||||
this.externalTitle = externalTitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl canCloseWithEscape(final boolean canCloseWithEscape) {
|
||||
this.canCloseWithEscape = canCloseWithEscape;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl pause(final boolean pause) {
|
||||
this.pause = pause;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl afterAction(final DialogAfterAction afterAction) {
|
||||
this.afterAction = afterAction;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl body(final List<? extends DialogBody> body) {
|
||||
this.body = List.copyOf(body);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl inputs(final List<? extends DialogInput> inputs) {
|
||||
this.inputs = List.copyOf(inputs);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogBase build() {
|
||||
return new DialogBaseImpl(this.title, this.externalTitle, this.canCloseWithEscape, this.pause, this.afterAction, this.body, this.inputs);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,186 @@
|
||||
package io.papermc.paper.registry.data.dialog;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import io.papermc.paper.adventure.AdventureCodecs;
|
||||
import io.papermc.paper.dialog.PaperDialog;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.data.dialog.action.DialogAction;
|
||||
import io.papermc.paper.registry.data.dialog.body.DialogBody;
|
||||
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.DialogInput;
|
||||
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 java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.dialog.CommonButtonData;
|
||||
import net.minecraft.server.dialog.Dialog;
|
||||
import net.minecraft.server.dialog.action.ParsedTemplate;
|
||||
import net.minecraft.server.dialog.body.PlainMessage;
|
||||
import net.minecraft.server.dialog.input.TextInput;
|
||||
import net.minecraft.util.ExtraCodecs;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
|
||||
import static io.papermc.paper.util.PaperCodecs.registryFileDecoderFor;
|
||||
|
||||
public final class PaperDialogCodecs {
|
||||
|
||||
private PaperDialogCodecs() {
|
||||
}
|
||||
|
||||
// dialog actions
|
||||
private static final MapCodec<DialogAction.CommandTemplateAction> COMMAND_TEMPLATE_ACTION_CODEC = RecordCodecBuilder.mapCodec(instance ->
|
||||
instance.group(Codec.STRING.fieldOf("template").forGetter(DialogAction.CommandTemplateAction::template))
|
||||
.apply(instance, DialogAction::commandTemplate)
|
||||
);
|
||||
private static final MapCodec<DialogAction.CustomClickAction> CUSTOM_ALL_ACTION_CODEC = RecordCodecBuilder.mapCodec(instance ->
|
||||
instance.group(AdventureCodecs.KEY_CODEC.fieldOf("id").forGetter(DialogAction.CustomClickAction::id), AdventureCodecs.BINARY_TAG_HOLDER_COMPOUND_CODEC.optionalFieldOf("additions").forGetter(action -> Optional.ofNullable(action.additions())))
|
||||
.apply(instance, (key, binaryTagHolder) -> DialogAction.customClick(key, binaryTagHolder.orElse(null)))
|
||||
);
|
||||
private static final Map<AdventureCodecs.ClickEventType, MapCodec<DialogAction.StaticAction>> STATIC_ACTION_CODECS = Arrays.stream(AdventureCodecs.CLICK_EVENT_TYPES.get()).collect(Collectors.toMap(Function.identity(), type -> type.codec().xmap(DialogAction::staticAction, DialogAction.StaticAction::value)));
|
||||
private static final Registry<MapCodec<? extends DialogAction>> DIALOG_ACTION_TYPES = Util.make(() -> {
|
||||
final MappedRegistry<MapCodec<? extends DialogAction>> registry = new MappedRegistry<>(ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(ResourceLocation.PAPER_NAMESPACE, "dialog_action_type")), Lifecycle.experimental());
|
||||
STATIC_ACTION_CODECS.forEach((clickType, actionCodec) -> {
|
||||
Registry.register(registry, clickType.getSerializedName(), actionCodec);
|
||||
});
|
||||
Registry.register(registry, "dynamic/run_command", COMMAND_TEMPLATE_ACTION_CODEC);
|
||||
Registry.register(registry, "dynamic/custom", CUSTOM_ALL_ACTION_CODEC);
|
||||
return registry.freeze();
|
||||
});
|
||||
private static final Function<DialogAction, MapCodec<? extends DialogAction>> GET_DIALOG_ACTION_TYPE = dialogAction -> switch (dialogAction) {
|
||||
case DialogAction.CommandTemplateAction $ -> COMMAND_TEMPLATE_ACTION_CODEC;
|
||||
case DialogAction.CustomClickAction $ -> CUSTOM_ALL_ACTION_CODEC;
|
||||
case DialogAction.StaticAction action -> STATIC_ACTION_CODECS.get(AdventureCodecs.GET_CLICK_EVENT_TYPE.apply(action.value()));
|
||||
};
|
||||
private static final Codec<DialogAction> DIALOG_ACTION_CODEC = DIALOG_ACTION_TYPES.byNameCodec().dispatch(GET_DIALOG_ACTION_TYPE, Function.identity());
|
||||
|
||||
// buttons
|
||||
public static final Codec<ActionButton> ACTION_BUTTON_CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
AdventureCodecs.COMPONENT_CODEC.fieldOf("label").forGetter(ActionButton::label),
|
||||
AdventureCodecs.COMPONENT_CODEC.optionalFieldOf("tooltip").forGetter(button -> Optional.ofNullable(button.tooltip())),
|
||||
Dialog.WIDTH_CODEC.optionalFieldOf("width", CommonButtonData.DEFAULT_WIDTH).forGetter(ActionButton::width),
|
||||
DIALOG_ACTION_CODEC.optionalFieldOf("action").forGetter(button -> Optional.ofNullable(button.action()))
|
||||
).apply(instance, (label, tooltip, width, action) -> ActionButton.create(label, tooltip.orElse(null), width, action.orElse(null))));
|
||||
|
||||
// dialog bodies
|
||||
private static final MapCodec<PlainMessageDialogBody> PLAIN_MESSAGE_BODY_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||
AdventureCodecs.COMPONENT_CODEC.fieldOf("contents").forGetter(PlainMessageDialogBody::contents),
|
||||
Dialog.WIDTH_CODEC.optionalFieldOf("width", PlainMessage.DEFAULT_WIDTH).forGetter(PlainMessageDialogBody::width)
|
||||
).apply(instance, DialogBody::plainMessage)
|
||||
);
|
||||
private static final Codec<PlainMessageDialogBody> SIMPLE_PLAIN_MESSAGE_BODY_CODEC = Codec.withAlternative(PLAIN_MESSAGE_BODY_CODEC.codec(), AdventureCodecs.COMPONENT_CODEC, component -> DialogBody.plainMessage(component, PlainMessage.DEFAULT_WIDTH));
|
||||
private static final MapCodec<ItemDialogBody> ITEM_BODY_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||
ItemStack.STRICT_CODEC.xmap(CraftItemStack::asBukkitCopy, CraftItemStack::asNMSCopy).fieldOf("item").forGetter(ItemDialogBody::item),
|
||||
SIMPLE_PLAIN_MESSAGE_BODY_CODEC.optionalFieldOf("description").forGetter(body -> Optional.ofNullable(body.description())),
|
||||
Codec.BOOL.optionalFieldOf("show_decorations", true).forGetter(ItemDialogBody::showDecorations),
|
||||
Codec.BOOL.optionalFieldOf("show_tooltip", true).forGetter(ItemDialogBody::showTooltip),
|
||||
ExtraCodecs.intRange(1, 256).optionalFieldOf("width", 16).forGetter(ItemDialogBody::width),
|
||||
ExtraCodecs.intRange(1, 256).optionalFieldOf("height", 16).forGetter(ItemDialogBody::height)
|
||||
).apply(instance, (itemStack, plainMessageBody, showDecorations, showTooltip, width, height) -> DialogBody.item(itemStack, plainMessageBody.orElse(null), showDecorations, showTooltip, width, height))
|
||||
);
|
||||
private static final Registry<MapCodec<? extends DialogBody>> DIALOG_BODY_TYPES = Util.make(() -> {
|
||||
final MappedRegistry<MapCodec<? extends DialogBody>> registry = new MappedRegistry<>(ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(ResourceLocation.PAPER_NAMESPACE, "dialog_body_type")), Lifecycle.experimental());
|
||||
Registry.register(registry, "item", ITEM_BODY_CODEC);
|
||||
Registry.register(registry, "plain_message", PLAIN_MESSAGE_BODY_CODEC);
|
||||
return registry.freeze();
|
||||
});
|
||||
private static final Function<DialogBody, MapCodec<? extends DialogBody>> GET_DIALOG_BODY_TYPE = dialogAction -> switch (dialogAction) {
|
||||
case PlainMessageDialogBody $ -> PLAIN_MESSAGE_BODY_CODEC;
|
||||
case ItemDialogBody $ -> ITEM_BODY_CODEC;
|
||||
};
|
||||
private static final Codec<DialogBody> DIALOG_BODY_CODEC = DIALOG_BODY_TYPES.byNameCodec().dispatch(GET_DIALOG_BODY_TYPE, Function.identity());
|
||||
private static final Codec<List<DialogBody>> DIALOG_BODY_LIST_CODEC = ExtraCodecs.compactListCodec(DIALOG_BODY_CODEC);
|
||||
|
||||
// input types
|
||||
private static final MapCodec<BooleanDialogInput> BOOLEAN_DIALOG_INPUT_TYPE_MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||
ParsedTemplate.VARIABLE_CODEC.fieldOf("key").forGetter(DialogInput::key),
|
||||
AdventureCodecs.COMPONENT_CODEC.fieldOf("label").forGetter(BooleanDialogInput::label),
|
||||
Codec.BOOL.optionalFieldOf("initial", false).forGetter(BooleanDialogInput::initial),
|
||||
Codec.STRING.optionalFieldOf("on_true", "true").forGetter(BooleanDialogInput::onTrue),
|
||||
Codec.STRING.optionalFieldOf("on_false", "false").forGetter(BooleanDialogInput::onFalse)
|
||||
).apply(instance, DialogInput::bool));
|
||||
private static final MapCodec<NumberRangeDialogInput> NUMBER_RANGE_INPUT_MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||
ParsedTemplate.VARIABLE_CODEC.fieldOf("key").forGetter(DialogInput::key),
|
||||
Dialog.WIDTH_CODEC.optionalFieldOf("width", PlainMessage.DEFAULT_WIDTH).forGetter(NumberRangeDialogInput::width),
|
||||
AdventureCodecs.COMPONENT_CODEC.fieldOf("label").forGetter(NumberRangeDialogInput::label),
|
||||
Codec.STRING.optionalFieldOf("label_format", "options.generic_value").forGetter(NumberRangeDialogInput::labelFormat),
|
||||
Codec.FLOAT.fieldOf("start").forGetter(NumberRangeDialogInput::start),
|
||||
Codec.FLOAT.fieldOf("end").forGetter(NumberRangeDialogInput::end),
|
||||
Codec.FLOAT.optionalFieldOf("initial").forGetter(type -> Optional.ofNullable(type.initial())),
|
||||
ExtraCodecs.POSITIVE_FLOAT.optionalFieldOf("step").forGetter(type -> Optional.ofNullable(type.step()))
|
||||
).apply(instance, (key, width, label, labelFormat, start, end, initial, step) -> DialogInput.numberRange(key, width, label, labelFormat, start, end, initial.orElse(null), step.orElse(null))));
|
||||
private static final Codec<SingleOptionDialogInput.OptionEntry> SINGLE_OPTION_DIALOG_INPUT_ENTRY_FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
Codec.STRING.fieldOf("id").forGetter(SingleOptionDialogInput.OptionEntry::id),
|
||||
AdventureCodecs.COMPONENT_CODEC.optionalFieldOf("display").forGetter(entry -> Optional.ofNullable(entry.display())),
|
||||
Codec.BOOL.optionalFieldOf("initial", false).forGetter(SingleOptionDialogInput.OptionEntry::initial)
|
||||
).apply(instance, (id, display, initial) -> SingleOptionDialogInput.OptionEntry.create(id, display.orElse(null), initial)));
|
||||
private static final Codec<SingleOptionDialogInput.OptionEntry> SINGLE_OPTION_DIALOG_INPUT_ENTRY_CODEC = Codec.withAlternative(
|
||||
SINGLE_OPTION_DIALOG_INPUT_ENTRY_FULL_CODEC, Codec.STRING, string -> SingleOptionDialogInput.OptionEntry.create(string, null, false)
|
||||
);
|
||||
private static final MapCodec<SingleOptionDialogInput> SINGLE_OPTION_DIALOG_INPUT_TYPE_MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||
ParsedTemplate.VARIABLE_CODEC.fieldOf("key").forGetter(DialogInput::key),
|
||||
Dialog.WIDTH_CODEC.optionalFieldOf("width", PlainMessage.DEFAULT_WIDTH).forGetter(SingleOptionDialogInput::width),
|
||||
ExtraCodecs.nonEmptyList(SINGLE_OPTION_DIALOG_INPUT_ENTRY_CODEC.listOf()).fieldOf("options").forGetter(SingleOptionDialogInput::entries),
|
||||
AdventureCodecs.COMPONENT_CODEC.fieldOf("label").forGetter(SingleOptionDialogInput::label),
|
||||
Codec.BOOL.optionalFieldOf("label_visible", true).forGetter(SingleOptionDialogInput::labelVisible)
|
||||
).apply(instance, DialogInput::singleOption));
|
||||
private static final Codec<TextDialogInput.MultilineOptions> TEXT_DIALOG_INPUT_MULTILINE_OPTIONS_CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
ExtraCodecs.POSITIVE_INT.optionalFieldOf("max_lines").forGetter(options -> Optional.ofNullable(options.maxLines())),
|
||||
ExtraCodecs.intRange(1, TextInput.MultilineOptions.MAX_HEIGHT).optionalFieldOf("height").forGetter(options -> Optional.ofNullable(options.height()))
|
||||
).apply(instance, (maxLines, height) -> TextDialogInput.MultilineOptions.create(maxLines.orElse(null), height.orElse(null))));
|
||||
private static final MapCodec<TextDialogInput> TEXT_DIALOG_INPUT_TYPE_MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||
ParsedTemplate.VARIABLE_CODEC.fieldOf("key").forGetter(DialogInput::key),
|
||||
Dialog.WIDTH_CODEC.optionalFieldOf("width", PlainMessage.DEFAULT_WIDTH).forGetter(TextDialogInput::width),
|
||||
AdventureCodecs.COMPONENT_CODEC.fieldOf("label").forGetter(TextDialogInput::label),
|
||||
Codec.BOOL.optionalFieldOf("label_visible", true).forGetter(TextDialogInput::labelVisible),
|
||||
Codec.STRING.optionalFieldOf("initial", "").forGetter(TextDialogInput::initial),
|
||||
ExtraCodecs.POSITIVE_INT.optionalFieldOf("max_length", 32).forGetter(TextDialogInput::maxLength),
|
||||
TEXT_DIALOG_INPUT_MULTILINE_OPTIONS_CODEC.optionalFieldOf("multiline").forGetter(inputType -> Optional.ofNullable(inputType.multiline()))
|
||||
).apply(instance, (key, width, label, labelVisible, initial, maxLength, multilineOptions) ->
|
||||
DialogInput.text(key, width, label, labelVisible, initial, maxLength, multilineOptions.orElse(null))
|
||||
));
|
||||
private static final Registry<MapCodec<? extends DialogInput>> DIALOG_INPUT_TYPES = Util.make(() -> {
|
||||
final MappedRegistry<MapCodec<? extends DialogInput>> registry = new MappedRegistry<>(ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(ResourceLocation.PAPER_NAMESPACE, "dialog_input_type")), Lifecycle.experimental());
|
||||
Registry.register(registry, "boolean", BOOLEAN_DIALOG_INPUT_TYPE_MAP_CODEC);
|
||||
Registry.register(registry, "number_range", NUMBER_RANGE_INPUT_MAP_CODEC);
|
||||
Registry.register(registry, "single_option", SINGLE_OPTION_DIALOG_INPUT_TYPE_MAP_CODEC);
|
||||
Registry.register(registry, "text", TEXT_DIALOG_INPUT_TYPE_MAP_CODEC);
|
||||
return registry.freeze();
|
||||
});
|
||||
private static final Function<DialogInput, MapCodec<? extends DialogInput>> GET_DIALOG_INPUT_TYPE_TYPE = dialogAction -> switch (dialogAction) {
|
||||
case TextDialogInput $ -> TEXT_DIALOG_INPUT_TYPE_MAP_CODEC;
|
||||
case SingleOptionDialogInput $ -> SINGLE_OPTION_DIALOG_INPUT_TYPE_MAP_CODEC;
|
||||
case NumberRangeDialogInput $ -> NUMBER_RANGE_INPUT_MAP_CODEC;
|
||||
case BooleanDialogInput $ -> BOOLEAN_DIALOG_INPUT_TYPE_MAP_CODEC;
|
||||
};
|
||||
private static final Codec<DialogInput> DIALOG_INPUT_CODEC = DIALOG_INPUT_TYPES.byNameCodec().dispatchMap(GET_DIALOG_INPUT_TYPE_TYPE, Function.identity()).codec();
|
||||
|
||||
// dialog base / common dialog data
|
||||
public static final Codec<DialogBase> DIALOG_BASE_CODEC = RecordCodecBuilder.<DialogBase>mapCodec(instance -> instance.group(
|
||||
AdventureCodecs.COMPONENT_CODEC.fieldOf("title").forGetter(DialogBase::title),
|
||||
AdventureCodecs.COMPONENT_CODEC.optionalFieldOf("external_title").forGetter(base -> Optional.ofNullable(base.externalTitle())),
|
||||
Codec.BOOL.optionalFieldOf("can_close_with_escape", true).forGetter(DialogBase::canCloseWithEscape),
|
||||
Codec.BOOL.optionalFieldOf("pause", true).forGetter(DialogBase::pause),
|
||||
AdventureCodecs.indexCodec(DialogBase.DialogAfterAction.NAMES).optionalFieldOf("after_action", DialogBase.DialogAfterAction.CLOSE).forGetter(DialogBase::afterAction),
|
||||
DIALOG_BODY_LIST_CODEC.optionalFieldOf("body", List.of()).forGetter(DialogBase::body),
|
||||
DIALOG_INPUT_CODEC.listOf().optionalFieldOf("inputs", List.of()).forGetter(DialogBase::inputs)
|
||||
).apply(instance, (title, externalTitle, canCloseWithEsc, pause, afterAction, body, inputs) -> DialogBase.create(title, externalTitle.orElse(null), canCloseWithEsc, pause, afterAction, body, inputs))
|
||||
).codec();
|
||||
|
||||
public static final Codec<io.papermc.paper.dialog.Dialog> DIALOG_CODEC = Codec.of(Dialog.CODEC.comap(PaperDialog::bukkitToMinecraftHolder), registryFileDecoderFor(Dialog.DIRECT_CODEC, PaperDialog::minecraftHolderToBukkit, RegistryKey.DIALOG, true));
|
||||
}
|
@@ -0,0 +1,155 @@
|
||||
package io.papermc.paper.registry.data.dialog;
|
||||
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import io.papermc.paper.adventure.providers.ClickCallbackProviderImpl;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.registry.data.dialog.action.CommandTemplateActionImpl;
|
||||
import io.papermc.paper.registry.data.dialog.action.CustomClickActionImpl;
|
||||
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.action.StaticActionImpl;
|
||||
import io.papermc.paper.registry.data.dialog.body.ItemDialogBody;
|
||||
import io.papermc.paper.registry.data.dialog.body.ItemDialogBodyImpl;
|
||||
import io.papermc.paper.registry.data.dialog.body.PlainMessageBodyImpl;
|
||||
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.BooleanDialogInputImpl;
|
||||
import io.papermc.paper.registry.data.dialog.input.NumberRangeDialogInput;
|
||||
import io.papermc.paper.registry.data.dialog.input.NumberRangeDialogInputImpl;
|
||||
import io.papermc.paper.registry.data.dialog.input.SingleOptionDialogInput;
|
||||
import io.papermc.paper.registry.data.dialog.input.SingleOptionDialogInputImpl;
|
||||
import io.papermc.paper.registry.data.dialog.input.TextDialogInput;
|
||||
import io.papermc.paper.registry.data.dialog.input.TextDialogInputImpl;
|
||||
import io.papermc.paper.registry.data.dialog.type.ConfirmationType;
|
||||
import io.papermc.paper.registry.data.dialog.type.ConfirmationTypeImpl;
|
||||
import io.papermc.paper.registry.data.dialog.type.DialogListType;
|
||||
import io.papermc.paper.registry.data.dialog.type.DialogListTypeImpl;
|
||||
import io.papermc.paper.registry.data.dialog.type.MultiActionType;
|
||||
import io.papermc.paper.registry.data.dialog.type.MultiActionTypeImpl;
|
||||
import io.papermc.paper.registry.data.dialog.type.NoticeType;
|
||||
import io.papermc.paper.registry.data.dialog.type.NoticeTypeImpl;
|
||||
import io.papermc.paper.registry.data.dialog.type.ServerLinksType;
|
||||
import io.papermc.paper.registry.data.dialog.type.ServerLinksTypeImpl;
|
||||
import io.papermc.paper.registry.set.RegistrySet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
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 net.minecraft.core.UUIDUtil;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public final class PaperDialogInstancesProvider implements DialogInstancesProvider {
|
||||
|
||||
@Override
|
||||
public DialogBase.Builder dialogBaseBuilder(final Component title) {
|
||||
return new DialogBaseImpl.BuilderImpl(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionButton.Builder actionButtonBuilder(final Component label) {
|
||||
return new ActionButtonImpl.BuilderImpl(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogAction.CustomClickAction register(final DialogActionCallback callback, final ClickCallback.Options options) {
|
||||
final UUID id = ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.addCallback(UUID.randomUUID(), callback, options);
|
||||
final CompoundTag tag = new CompoundTag();
|
||||
tag.store(ClickCallbackProviderImpl.ID_KEY, UUIDUtil.CODEC, id);
|
||||
return DialogAction.customClick(ClickCallbackProviderImpl.DIALOG_CLICK_CALLBACK_KEY, BinaryTagHolder.encode(tag, PaperAdventure.NBT_CODEC));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogAction.StaticAction staticAction(final ClickEvent value) {
|
||||
return new StaticActionImpl(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogAction.CommandTemplateAction commandTemplate(final String template) {
|
||||
return new CommandTemplateActionImpl(template);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogAction.CustomClickAction customClick(final Key id, final @Nullable BinaryTagHolder additions) {
|
||||
return new CustomClickActionImpl(id, additions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDialogBody.Builder itemDialogBodyBuilder(final ItemStack itemStack) {
|
||||
return new ItemDialogBodyImpl.BuilderImpl(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlainMessageDialogBody plainMessageDialogBody(final Component component) {
|
||||
return new PlainMessageBodyImpl(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlainMessageDialogBody plainMessageDialogBody(final Component component, final int width) {
|
||||
return new PlainMessageBodyImpl(component, width);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanDialogInput.Builder booleanBuilder(final String key, final Component label) {
|
||||
return new BooleanDialogInputImpl.BuilderImpl(key, label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberRangeDialogInput.Builder numberRangeBuilder(final String key, final Component label, final float start, final float end) {
|
||||
return new NumberRangeDialogInputImpl.BuilderImpl(key, label, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleOptionDialogInput.Builder singleOptionBuilder(final String key, final Component label, final List<SingleOptionDialogInput.OptionEntry> entries) {
|
||||
return new SingleOptionDialogInputImpl.BuilderImpl(key, entries, label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleOptionDialogInput.OptionEntry singleOptionEntry(final String id, final @Nullable Component display, final boolean initial) {
|
||||
return new SingleOptionDialogInputImpl.SingleOptionEntryImpl(id, display, initial);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextDialogInput.Builder textBuilder(final String key, final Component label) {
|
||||
return new TextDialogInputImpl.BuilderImpl(key, label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextDialogInput.MultilineOptions multilineOptions(final @Nullable Integer maxLines, final @Nullable Integer height) {
|
||||
return new TextDialogInputImpl.MultilineOptionsImpl(maxLines, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfirmationType confirmation(final ActionButton yesButton, final ActionButton noButton) {
|
||||
return new ConfirmationTypeImpl(yesButton, noButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogListType.Builder dialogList(final RegistrySet<Dialog> dialogs) {
|
||||
return new DialogListTypeImpl.BuilderImpl(dialogs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiActionType.Builder multiAction(final List<ActionButton> actions) {
|
||||
return new MultiActionTypeImpl.BuilderImpl(actions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NoticeType notice() {
|
||||
return new NoticeTypeImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NoticeType notice(final ActionButton action) {
|
||||
return new NoticeTypeImpl(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerLinksType serverLinks(final @Nullable ActionButton exitAction, final int columns, final int buttonWidth) {
|
||||
return new ServerLinksTypeImpl(exitAction, columns, buttonWidth);
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
package io.papermc.paper.registry.data.dialog;
|
||||
|
||||
import io.papermc.paper.registry.PaperRegistryBuilder;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.data.dialog.type.DialogType;
|
||||
import io.papermc.paper.registry.data.util.Conversions;
|
||||
import io.papermc.paper.registry.set.RegistryValueSetBuilder;
|
||||
import io.papermc.paper.registry.set.RegistryValueSetBuilderImpl;
|
||||
import net.minecraft.server.dialog.CommonDialogData;
|
||||
import net.minecraft.server.dialog.Dialog;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import static io.papermc.paper.registry.data.util.Checks.asArgument;
|
||||
import static io.papermc.paper.registry.data.util.Checks.asConfigured;
|
||||
|
||||
public class PaperDialogRegistryEntry implements DialogRegistryEntry {
|
||||
|
||||
protected @Nullable DialogBase dialogBase;
|
||||
protected @Nullable DialogType dialogType;
|
||||
|
||||
protected final Conversions conversions;
|
||||
|
||||
public PaperDialogRegistryEntry(
|
||||
final Conversions conversions,
|
||||
final @Nullable Dialog internal
|
||||
) {
|
||||
this.conversions = conversions;
|
||||
if (internal == null) return;
|
||||
|
||||
final CommonDialogData common = internal.common();
|
||||
this.dialogBase = conversions.convert(common, PaperDialogCodecs.DIALOG_BASE_CODEC, CommonDialogData.MAP_CODEC.codec());
|
||||
|
||||
this.dialogType = PaperDialogs.extractSpecialty(internal, conversions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogBase base() {
|
||||
return asConfigured(this.dialogBase, "dialogBase");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogType type() {
|
||||
return asConfigured(this.dialogType, "dialogSpecialty");
|
||||
}
|
||||
|
||||
public static final class PaperBuilder extends PaperDialogRegistryEntry implements Builder, PaperRegistryBuilder<Dialog, io.papermc.paper.dialog.Dialog> {
|
||||
|
||||
public PaperBuilder(final Conversions conversions, final @Nullable Dialog internal) {
|
||||
super(conversions, internal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryValueSetBuilder<io.papermc.paper.dialog.Dialog, Builder> registryValueSet() {
|
||||
return new RegistryValueSetBuilderImpl<>(RegistryKey.DIALOG, this.conversions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder base(final DialogBase dialogBase) {
|
||||
this.dialogBase = asArgument(dialogBase, "dialogBase");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder type(final DialogType dialogType) {
|
||||
this.dialogType = asArgument(dialogType, "dialogSpecialty");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog build() {
|
||||
return PaperDialogs.constructDialog(this.base(), this.type(), this.conversions);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
package io.papermc.paper.registry.data.dialog;
|
||||
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
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.DialogType;
|
||||
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.data.util.Conversions;
|
||||
import io.papermc.paper.registry.set.PaperRegistrySets;
|
||||
import io.papermc.paper.registry.set.RegistrySet;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.server.dialog.CommonDialogData;
|
||||
import net.minecraft.server.dialog.ConfirmationDialog;
|
||||
import net.minecraft.server.dialog.Dialog;
|
||||
import net.minecraft.server.dialog.DialogListDialog;
|
||||
import net.minecraft.server.dialog.MultiActionDialog;
|
||||
import net.minecraft.server.dialog.NoticeDialog;
|
||||
import net.minecraft.server.dialog.ServerLinksDialog;
|
||||
|
||||
public final class PaperDialogs {
|
||||
|
||||
private PaperDialogs() {
|
||||
}
|
||||
|
||||
public static DialogType extractSpecialty(final Dialog nmsDialog, final Conversions conversions) {
|
||||
final Function<net.minecraft.server.dialog.ActionButton, ActionButton> convertButton = button -> conversions.convert(button, PaperDialogCodecs.ACTION_BUTTON_CODEC, net.minecraft.server.dialog.ActionButton.CODEC);
|
||||
return switch (nmsDialog) {
|
||||
case final ConfirmationDialog conf ->
|
||||
DialogType.confirmation(convertButton.apply(conf.yesButton()), convertButton.apply(conf.noButton()));
|
||||
case final DialogListDialog list -> {
|
||||
final RegistrySet<io.papermc.paper.dialog.Dialog> apiSet = PaperRegistrySets.convertToApiWithDirects(RegistryKey.DIALOG, list.dialogs());
|
||||
yield DialogType.dialogList(
|
||||
apiSet,
|
||||
list.exitAction().map(convertButton).orElse(null),
|
||||
list.columns(),
|
||||
list.buttonWidth()
|
||||
);
|
||||
}
|
||||
case final MultiActionDialog multi ->
|
||||
DialogType.multiAction(multi.actions().stream().map(convertButton).toList(), multi.exitAction().map(convertButton).orElse(null), multi.columns());
|
||||
case final NoticeDialog notice -> DialogType.notice(convertButton.apply(notice.action()));
|
||||
case final ServerLinksDialog links ->
|
||||
DialogType.serverLinks(links.exitAction().map(convertButton).orElse(null), links.columns(), links.buttonWidth());
|
||||
default -> throw new IllegalArgumentException("Unsupported dialog type: " + nmsDialog.getClass().getName());
|
||||
};
|
||||
}
|
||||
|
||||
public static Dialog constructDialog(final DialogBase dialogBase, final DialogType dialogType, final Conversions conversions) {
|
||||
final Function<ActionButton, net.minecraft.server.dialog.ActionButton> convertButton = button -> conversions.convert(button, net.minecraft.server.dialog.ActionButton.CODEC, PaperDialogCodecs.ACTION_BUTTON_CODEC);
|
||||
final CommonDialogData common = conversions.convert(dialogBase, CommonDialogData.MAP_CODEC.codec(), PaperDialogCodecs.DIALOG_BASE_CODEC);
|
||||
switch (dialogType) {
|
||||
case final ConfirmationType conf -> {
|
||||
return new ConfirmationDialog(common, convertButton.apply(conf.yesButton()), convertButton.apply(conf.noButton()));
|
||||
}
|
||||
case final DialogListType list -> {
|
||||
return new DialogListDialog(
|
||||
common,
|
||||
PaperRegistrySets.convertToNmsWithDirects(Registries.DIALOG, conversions.lookup(), list.dialogs()),
|
||||
Optional.ofNullable(list.exitAction()).map(convertButton),
|
||||
list.columns(),
|
||||
list.buttonWidth()
|
||||
);
|
||||
}
|
||||
case final MultiActionType multi -> {
|
||||
return new MultiActionDialog(
|
||||
common,
|
||||
multi.actions().stream().map(convertButton).toList(),
|
||||
Optional.ofNullable(multi.exitAction()).map(convertButton),
|
||||
multi.columns()
|
||||
);
|
||||
}
|
||||
case final NoticeType notice -> {
|
||||
return new NoticeDialog(common, convertButton.apply(notice.action()));
|
||||
}
|
||||
case final ServerLinksType links -> {
|
||||
return new ServerLinksDialog(common, Optional.ofNullable(links.exitAction()).map(convertButton), links.columns(), links.buttonWidth());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package io.papermc.paper.registry.data.dialog.action;
|
||||
|
||||
import net.minecraft.commands.functions.StringTemplate;
|
||||
|
||||
public record CommandTemplateActionImpl(String template) implements DialogAction.CommandTemplateAction {
|
||||
|
||||
public CommandTemplateActionImpl {
|
||||
StringTemplate.fromString(template);
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package io.papermc.paper.registry.data.dialog.action;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.nbt.api.BinaryTagHolder;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public record CustomClickActionImpl(Key id, @Nullable BinaryTagHolder additions) implements DialogAction.CustomClickAction {
|
||||
|
||||
public CustomClickActionImpl {
|
||||
if (additions != null) {
|
||||
try {
|
||||
final Tag tag = additions.get(PaperAdventure.NBT_CODEC);
|
||||
Preconditions.checkArgument(tag instanceof CompoundTag, "Additions must be a compound tag");
|
||||
} catch (final CommandSyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package io.papermc.paper.registry.data.dialog.action;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
|
||||
public record StaticActionImpl(ClickEvent value) implements DialogAction.StaticAction {
|
||||
|
||||
public StaticActionImpl {
|
||||
Preconditions.checkArgument(value.action().readable(), "action must be readable");
|
||||
}
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
package io.papermc.paper.registry.data.dialog.body;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public record ItemDialogBodyImpl(
|
||||
ItemStack item, @Nullable PlainMessageDialogBody description, boolean showDecorations, boolean showTooltip, int width, int height
|
||||
) implements ItemDialogBody {
|
||||
|
||||
public static final class BuilderImpl implements ItemDialogBody.Builder {
|
||||
|
||||
private final ItemStack item;
|
||||
private @Nullable PlainMessageDialogBody description;
|
||||
private boolean showDecorations = true;
|
||||
private boolean showTooltip = true;
|
||||
private int width = 16;
|
||||
private int height = 16;
|
||||
|
||||
public BuilderImpl(final ItemStack item) {
|
||||
net.minecraft.world.item.ItemStack.validateStrict(CraftItemStack.unwrap(item)).getOrThrow();
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDialogBody.Builder description(final @Nullable PlainMessageDialogBody description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDialogBody.Builder showDecorations(final boolean showDecorations) {
|
||||
this.showDecorations = showDecorations;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDialogBody.Builder showTooltip(final boolean showTooltip) {
|
||||
this.showTooltip = showTooltip;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDialogBody.Builder width(final int width) {
|
||||
Preconditions.checkArgument(width >= 1 && width <= 256, "Width must be between 1 and 256");
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDialogBody.Builder height(final int height) {
|
||||
Preconditions.checkArgument(this.width >= 1 && this.width <= 256, "Width must be between 1 and 256");
|
||||
this.height = height;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDialogBody build() {
|
||||
return new ItemDialogBodyImpl(this.item, this.description, this.showDecorations, this.showTooltip, this.width, this.height);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package io.papermc.paper.registry.data.dialog.body;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.server.dialog.body.PlainMessage;
|
||||
|
||||
public record PlainMessageBodyImpl(Component contents, int width) implements PlainMessageDialogBody {
|
||||
|
||||
public PlainMessageBodyImpl {
|
||||
Preconditions.checkArgument(width >= 1 && width <= 1024, "Width must be between 1 and 1024");
|
||||
}
|
||||
|
||||
public PlainMessageBodyImpl(final Component contents) {
|
||||
this(contents, PlainMessage.DEFAULT_WIDTH);
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package io.papermc.paper.registry.data.dialog.input;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.commands.functions.StringTemplate;
|
||||
|
||||
public record BooleanDialogInputImpl(String key, Component label, boolean initial, String onTrue, String onFalse) implements BooleanDialogInput {
|
||||
|
||||
public static final class BuilderImpl implements BooleanDialogInput.Builder {
|
||||
|
||||
private final String key;
|
||||
private final Component label;
|
||||
private boolean initial = false;
|
||||
private String onTrue = "true";
|
||||
private String onFalse = "false";
|
||||
|
||||
public BuilderImpl(final String key, final Component label) {
|
||||
Preconditions.checkArgument(StringTemplate.isValidVariableName(key), "key must be a valid input name");
|
||||
this.key = key;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanDialogInput.Builder initial(final boolean initial) {
|
||||
this.initial = initial;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanDialogInput.Builder onTrue(final String onTrue) {
|
||||
this.onTrue = onTrue;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanDialogInput.Builder onFalse(final String onFalse) {
|
||||
this.onFalse = onFalse;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanDialogInput build() {
|
||||
return new BooleanDialogInputImpl(this.key, this.label, this.initial, this.onTrue, this.onFalse);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
package io.papermc.paper.registry.data.dialog.input;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.commands.functions.StringTemplate;
|
||||
import net.minecraft.server.dialog.body.PlainMessage;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public record NumberRangeDialogInputImpl(
|
||||
String key,
|
||||
int width,
|
||||
Component label,
|
||||
String labelFormat,
|
||||
float start,
|
||||
float end,
|
||||
@Nullable Float initial,
|
||||
@Nullable Float step
|
||||
) implements NumberRangeDialogInput {
|
||||
|
||||
public static final class BuilderImpl implements NumberRangeDialogInput.Builder {
|
||||
|
||||
private final String key;
|
||||
private final Component label;
|
||||
private final float start;
|
||||
private final float end;
|
||||
private int width = PlainMessage.DEFAULT_WIDTH;
|
||||
private String labelFormat = "options.generic_value";
|
||||
private @Nullable Float initial = null;
|
||||
private @Nullable Float step = null;
|
||||
|
||||
public BuilderImpl(final String key, final Component label, final float start, final float end) {
|
||||
Preconditions.checkArgument(StringTemplate.isValidVariableName(key), "key must be a valid input name");
|
||||
this.key = key;
|
||||
this.label = label;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl width(final int width) {
|
||||
Preconditions.checkArgument(width >= 1 && width <= 1024, "width must be between 1 and 1024");
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl labelFormat(final String labelFormat) {
|
||||
this.labelFormat = labelFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl initial(final @Nullable Float initial) {
|
||||
if (initial != null) {
|
||||
Preconditions.checkArgument(initial >= this.start && initial <= this.end, "initial must be within the range");
|
||||
}
|
||||
this.initial = initial;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl step(final @Nullable Float step) {
|
||||
Preconditions.checkArgument(step == null || step > 0, "step must be null or greater than 0");
|
||||
this.step = step;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberRangeDialogInput build() {
|
||||
return new NumberRangeDialogInputImpl(this.key, this.width, this.label, this.labelFormat, this.start, this.end, this.initial, this.step);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package io.papermc.paper.registry.data.dialog.input;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.List;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.commands.functions.StringTemplate;
|
||||
import net.minecraft.server.dialog.body.PlainMessage;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record SingleOptionDialogInputImpl(
|
||||
String key,
|
||||
int width,
|
||||
List<SingleOptionDialogInput.OptionEntry> entries,
|
||||
Component label,
|
||||
boolean labelVisible
|
||||
) implements SingleOptionDialogInput {
|
||||
|
||||
public SingleOptionDialogInputImpl {
|
||||
entries = List.copyOf(entries);
|
||||
}
|
||||
|
||||
public record SingleOptionEntryImpl(String id, @Nullable Component display, boolean initial) implements OptionEntry {
|
||||
}
|
||||
|
||||
public static final class BuilderImpl implements SingleOptionDialogInput.Builder {
|
||||
|
||||
private final String key;
|
||||
private int width = PlainMessage.DEFAULT_WIDTH;
|
||||
private final List<OptionEntry> entries;
|
||||
private final Component label;
|
||||
private boolean labelVisible = true;
|
||||
|
||||
public BuilderImpl(final String key, final List<OptionEntry> entries, final Component label) {
|
||||
Preconditions.checkArgument(StringTemplate.isValidVariableName(key), "key must be a valid input name");
|
||||
this.key = key;
|
||||
Preconditions.checkArgument(!entries.isEmpty(), "entries must not be empty");
|
||||
Preconditions.checkArgument(entries.stream().filter(OptionEntry::initial).count() <= 1, "only 1 option can be initially selected");
|
||||
this.entries = entries;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl width(final int width) {
|
||||
Preconditions.checkArgument(width >= 1 && width <= 1024, "width must be between 1 and 1024");
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderImpl labelVisible(final boolean labelVisible) {
|
||||
this.labelVisible = labelVisible;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleOptionDialogInput build() {
|
||||
return new SingleOptionDialogInputImpl(this.key, this.width, this.entries, this.label, this.labelVisible);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
package io.papermc.paper.registry.data.dialog.input;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.commands.functions.StringTemplate;
|
||||
import net.minecraft.server.dialog.body.PlainMessage;
|
||||
import net.minecraft.server.dialog.input.TextInput;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public record TextDialogInputImpl(
|
||||
String key,
|
||||
int width,
|
||||
Component label,
|
||||
boolean labelVisible,
|
||||
String initial,
|
||||
int maxLength,
|
||||
TextDialogInput.@Nullable MultilineOptions multiline
|
||||
) implements TextDialogInput {
|
||||
|
||||
public record MultilineOptionsImpl(@Nullable Integer maxLines, @Nullable Integer height) implements MultilineOptions {
|
||||
public MultilineOptionsImpl {
|
||||
Preconditions.checkArgument(maxLines == null || maxLines > 0, "maxLines must be null or greater than 0");
|
||||
Preconditions.checkArgument(height == null || (height >= 1 && height <= TextInput.MultilineOptions.MAX_HEIGHT), "height must be null or between 1 and 512");
|
||||
}
|
||||
}
|
||||
|
||||
public static final class BuilderImpl implements TextDialogInput.Builder {
|
||||
|
||||
private final String key;
|
||||
private int width = PlainMessage.DEFAULT_WIDTH;
|
||||
private final Component label;
|
||||
private boolean labelVisible = true;
|
||||
private String initial = "";
|
||||
private int maxLength = 32;
|
||||
private TextDialogInput.@Nullable MultilineOptions multiline = null;
|
||||
|
||||
public BuilderImpl(final String key, final Component label) {
|
||||
Preconditions.checkArgument(StringTemplate.isValidVariableName(key), "key must be a valid input name");
|
||||
this.key = key;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextDialogInput.Builder width(final int width) {
|
||||
Preconditions.checkArgument(width >= 1 && width <= 1024, "width must be between 1 and 1024");
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextDialogInput.Builder labelVisible(final boolean labelVisible) {
|
||||
this.labelVisible = labelVisible;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextDialogInput.Builder initial(final String initial) {
|
||||
this.initial = initial;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextDialogInput.Builder maxLength(final int maxLength) {
|
||||
Preconditions.checkArgument(maxLength > 0, "maxLength must be greater than 0");
|
||||
this.maxLength = maxLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextDialogInput.Builder multiline(final TextDialogInput.@Nullable MultilineOptions multiline) {
|
||||
this.multiline = multiline;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextDialogInput build() {
|
||||
Preconditions.checkState(this.initial.length() <= this.maxLength, "The initial value must be less than or equal to the maximum length.");
|
||||
return new TextDialogInputImpl(this.key, this.width, this.label, this.labelVisible, this.initial, this.maxLength, this.multiline);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package io.papermc.paper.registry.data.dialog.type;
|
||||
|
||||
import io.papermc.paper.registry.data.dialog.ActionButton;
|
||||
|
||||
public record ConfirmationTypeImpl(ActionButton yesButton, ActionButton noButton) implements ConfirmationType {
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
package io.papermc.paper.registry.data.dialog.type;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.registry.data.dialog.ActionButton;
|
||||
import io.papermc.paper.registry.set.RegistrySet;
|
||||
import net.minecraft.server.dialog.CommonButtonData;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public record DialogListTypeImpl(
|
||||
RegistrySet<Dialog> dialogs,
|
||||
@Nullable ActionButton exitAction,
|
||||
int columns,
|
||||
int buttonWidth
|
||||
) implements DialogListType {
|
||||
|
||||
public static final class BuilderImpl implements DialogListType.Builder {
|
||||
|
||||
private final RegistrySet<Dialog> dialogs;
|
||||
private @Nullable ActionButton exitAction;
|
||||
private int columns = 2;
|
||||
private int buttonWidth = CommonButtonData.DEFAULT_WIDTH;
|
||||
|
||||
public BuilderImpl(final RegistrySet<Dialog> dialogs) {
|
||||
this.dialogs = dialogs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogListType.Builder exitAction(final @Nullable ActionButton exitAction) {
|
||||
this.exitAction = exitAction;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogListType.Builder columns(final int columns) {
|
||||
Preconditions.checkArgument(columns > 0, "columns must be greater than 0");
|
||||
this.columns = columns;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogListType.Builder buttonWidth(final int buttonWidth) {
|
||||
Preconditions.checkArgument(buttonWidth >= 1 && buttonWidth <= 1024, "buttonWidth must be between 1 and 1024");
|
||||
this.buttonWidth = buttonWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogListType build() {
|
||||
return new DialogListTypeImpl(this.dialogs, this.exitAction, this.columns, this.buttonWidth);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package io.papermc.paper.registry.data.dialog.type;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.papermc.paper.registry.data.dialog.ActionButton;
|
||||
import java.util.List;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public record MultiActionTypeImpl(
|
||||
List<ActionButton> actions,
|
||||
@Nullable ActionButton exitAction,
|
||||
int columns
|
||||
) implements MultiActionType {
|
||||
|
||||
public MultiActionTypeImpl {
|
||||
actions = List.copyOf(actions);
|
||||
}
|
||||
|
||||
public static final class BuilderImpl implements MultiActionType.Builder {
|
||||
private final List<ActionButton> actions;
|
||||
private @Nullable ActionButton exitAction = null;
|
||||
private int columns = 2;
|
||||
|
||||
public BuilderImpl(final List<ActionButton> actions) {
|
||||
Preconditions.checkArgument(!actions.isEmpty(), "actions cannot be empty");
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder exitAction(final @org.jetbrains.annotations.Nullable ActionButton exitAction) {
|
||||
this.exitAction = exitAction;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder columns(final int columns) {
|
||||
Preconditions.checkArgument(columns > 0, "columns must be greater than 0");
|
||||
this.columns = columns;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiActionType build() {
|
||||
return new MultiActionTypeImpl(this.actions, this.exitAction, this.columns);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package io.papermc.paper.registry.data.dialog.type;
|
||||
|
||||
import io.papermc.paper.registry.data.dialog.ActionButton;
|
||||
import net.minecraft.server.dialog.CommonButtonData;
|
||||
|
||||
import static net.kyori.adventure.text.Component.translatable;
|
||||
|
||||
public record NoticeTypeImpl(ActionButton action) implements NoticeType {
|
||||
|
||||
public static final ActionButton DEFAULT_ACTION = ActionButton.builder(translatable("gui.ok")).width(CommonButtonData.DEFAULT_WIDTH).build();
|
||||
|
||||
public NoticeTypeImpl() {
|
||||
this(DEFAULT_ACTION);
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package io.papermc.paper.registry.data.dialog.type;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.papermc.paper.registry.data.dialog.ActionButton;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public record ServerLinksTypeImpl(
|
||||
@Nullable ActionButton exitAction,
|
||||
int columns,
|
||||
int buttonWidth
|
||||
) implements ServerLinksType {
|
||||
|
||||
public ServerLinksTypeImpl {
|
||||
Preconditions.checkArgument(columns > 0, "columns must be greater than 0");
|
||||
Preconditions.checkArgument(buttonWidth >= 1 && buttonWidth <= 1024, "buttonWidth must be between 1 and 1024");
|
||||
}
|
||||
}
|
@@ -1,12 +1,14 @@
|
||||
package io.papermc.paper.registry.data.util;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.JavaOps;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import io.papermc.paper.adventure.WrapperAwareSerializer;
|
||||
import io.papermc.paper.registry.PaperRegistries;
|
||||
import io.papermc.paper.registry.PaperRegistryBuilder;
|
||||
import io.papermc.paper.registry.PaperRegistryBuilderFactory;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.data.client.ClientTextureAsset;
|
||||
import io.papermc.paper.registry.entry.RegistryEntryMeta;
|
||||
import java.util.function.Consumer;
|
||||
@@ -36,10 +38,20 @@ public class Conversions {
|
||||
|
||||
private final RegistryOps.RegistryInfoLookup lookup;
|
||||
private final WrapperAwareSerializer serializer;
|
||||
private final RegistryOps<Object> javaOps;
|
||||
|
||||
public Conversions(final RegistryOps.RegistryInfoLookup lookup) {
|
||||
this.lookup = lookup;
|
||||
this.serializer = new WrapperAwareSerializer(() -> RegistryOps.create(JavaOps.INSTANCE, lookup));
|
||||
this.javaOps = RegistryOps.create(JavaOps.INSTANCE, lookup);
|
||||
}
|
||||
|
||||
public <OUT, IN> OUT convert(final IN in, final Codec<OUT> outCodec, final Codec<IN> inCodec) {
|
||||
final Object obj = inCodec.encodeStart(this.javaOps, in)
|
||||
.getOrThrow(s -> new RuntimeException("Failed to encode input: " + in + "; " + s));
|
||||
return outCodec.decode(this.javaOps, obj)
|
||||
.getOrThrow(s -> new RuntimeException("Failed to decode to output: " + obj + "; " + s))
|
||||
.getFirst();
|
||||
}
|
||||
|
||||
public RegistryOps.RegistryInfoLookup lookup() {
|
||||
@@ -74,20 +86,20 @@ public class Conversions {
|
||||
);
|
||||
}
|
||||
|
||||
private static <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> RegistryEntryMeta.Buildable<M, A, B> getDirectHolderBuildableMeta(final ResourceKey<? extends Registry<M>> registryKey) {
|
||||
private static <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> RegistryEntryMeta.Buildable<M, A, B> getDirectHolderBuildableMeta(final RegistryKey<A> registryKey) {
|
||||
final RegistryEntryMeta.Buildable<M, A, B> buildableMeta = PaperRegistries.getBuildableMeta(registryKey);
|
||||
Preconditions.checkArgument(buildableMeta.registryTypeMapper().supportsDirectHolders(), "Registry type mapper must support direct holders");
|
||||
return buildableMeta;
|
||||
}
|
||||
|
||||
public <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> A createApiInstanceFromBuilder(final ResourceKey<? extends Registry<M>> registryKey, final Consumer<? super PaperRegistryBuilderFactory<M, A, B>> value) {
|
||||
public <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> A createApiInstanceFromBuilder(final RegistryKey<A> registryKey, final Consumer<? super PaperRegistryBuilderFactory<M, A, B>> value) {
|
||||
final RegistryEntryMeta.Buildable<M, A, B> meta = getDirectHolderBuildableMeta(registryKey);
|
||||
final PaperRegistryBuilderFactory<M, A, B> builderFactory = this.createRegistryBuilderFactory(registryKey, meta);
|
||||
value.accept(builderFactory);
|
||||
return meta.registryTypeMapper().createBukkit(Holder.direct(builderFactory.requireBuilder().build()));
|
||||
}
|
||||
|
||||
public <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> Holder<M> createHolderFromBuilder(final ResourceKey<? extends Registry<M>> registryKey, final Consumer<? super PaperRegistryBuilderFactory<M, A, B>> value) {
|
||||
public <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> Holder<M> createHolderFromBuilder(final RegistryKey<A> registryKey, final Consumer<? super PaperRegistryBuilderFactory<M, A, B>> value) {
|
||||
final RegistryEntryMeta.Buildable<M, A, B> meta = getDirectHolderBuildableMeta(registryKey);
|
||||
final PaperRegistryBuilderFactory<M, A, B> builderFactory = this.createRegistryBuilderFactory(registryKey, meta);
|
||||
value.accept(builderFactory);
|
||||
@@ -95,11 +107,12 @@ public class Conversions {
|
||||
}
|
||||
|
||||
private <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> PaperRegistryBuilderFactory<M, A, B> createRegistryBuilderFactory(
|
||||
final ResourceKey<? extends Registry<M>> registryKey,
|
||||
final RegistryKey<A> registryKey,
|
||||
final RegistryEntryMeta.Buildable<M, A, B> buildableMeta
|
||||
) {
|
||||
final HolderLookup.RegistryLookup<M> lookupForBuilders = this.lookup.lookupForValueCopyViaBuilders().lookupOrThrow(registryKey);
|
||||
return new PaperRegistryBuilderFactory<>(registryKey, this, buildableMeta.builderFiller(), lookupForBuilders::getValueForCopying);
|
||||
final ResourceKey<? extends Registry<M>> resourceRegistryKey = PaperRegistries.registryToNms(registryKey);
|
||||
final HolderLookup.RegistryLookup<M> lookupForBuilders = this.lookup.lookupForValueCopyViaBuilders().lookupOrThrow(resourceRegistryKey);
|
||||
return new PaperRegistryBuilderFactory<>(resourceRegistryKey, this, buildableMeta.builderFiller(), lookupForBuilders::getValueForCopying);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -37,10 +37,6 @@ public class RegistryEntryBuilder<M, A extends Keyed> { // TODO remove Keyed
|
||||
return new RegistryEntryImpl<>(new RegistryEntryMeta.ApiOnly<>(this.mcKey, this.apiKey, apiRegistrySupplier));
|
||||
}
|
||||
|
||||
public CraftStage<M, A> craft(final Class<?> classToPreload, final BiFunction<? super NamespacedKey, M, ? extends A> minecraftToBukkit) {
|
||||
return new CraftStage<>(this.mcKey, this.apiKey, classToPreload, new RegistryTypeMapper<>(minecraftToBukkit));
|
||||
}
|
||||
|
||||
public CraftStage<M, A> craft(final Class<?> classToPreload, final Function<Holder<M>, ? extends A> minecraftToBukkit) {
|
||||
return this.craft(classToPreload, minecraftToBukkit, false);
|
||||
}
|
||||
|
@@ -18,9 +18,7 @@ import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public record NamedRegistryKeySetImpl<T extends Keyed, M>( // TODO remove Keyed
|
||||
TagKey<T> tagKey,
|
||||
HolderSet.Named<M> namedSet
|
||||
|
@@ -3,6 +3,7 @@ package io.papermc.paper.registry.set;
|
||||
import io.papermc.paper.registry.PaperRegistries;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import io.papermc.paper.util.Holderable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.minecraft.core.Holder;
|
||||
@@ -11,6 +12,7 @@ import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.RegistryOps;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.craftbukkit.CraftRegistry;
|
||||
|
||||
public final class PaperRegistrySets {
|
||||
|
||||
@@ -25,6 +27,30 @@ public final class PaperRegistrySets {
|
||||
}
|
||||
}
|
||||
|
||||
public static <A extends Keyed, M> HolderSet<M> convertToNmsWithDirects(final ResourceKey<? extends Registry<M>> resourceKey, final RegistryOps.RegistryInfoLookup lookup, final RegistrySet<A> registrySet) { // TODO remove Keyed
|
||||
if (registrySet instanceof NamedRegistryKeySetImpl<A, ?>) {
|
||||
return ((NamedRegistryKeySetImpl<A, M>) registrySet).namedSet();
|
||||
} else if (registrySet.isEmpty()) {
|
||||
return HolderSet.empty();
|
||||
} else if (registrySet instanceof final RegistryValueSet<A> valueSet) {
|
||||
final List<Holder<M>> directs = new ArrayList<>(valueSet.values().size());
|
||||
for (final A value : valueSet) {
|
||||
if (!(value instanceof final Holderable<?> holderable)) {
|
||||
throw new UnsupportedOperationException("Cannot convert a registry set containing non-holderable values");
|
||||
}
|
||||
directs.add(((Holderable<M>) holderable).getHolder());
|
||||
}
|
||||
return HolderSet.direct(directs);
|
||||
} else if (registrySet instanceof final RegistryKeySet<A> keySet) {
|
||||
final RegistryOps.RegistryInfo<M> registryInfo = lookup.lookup(resourceKey).orElseThrow();
|
||||
return HolderSet.direct(key -> {
|
||||
return registryInfo.getter().getOrThrow(PaperRegistries.toNms(key));
|
||||
}, keySet.values());
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Cannot convert a registry set of type " + registrySet.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static <A extends Keyed, M> RegistryKeySet<A> convertToApi(final RegistryKey<A> registryKey, final HolderSet<M> holders) { // TODO remove Keyed
|
||||
if (holders instanceof final HolderSet.Named<M> named) {
|
||||
return new NamedRegistryKeySetImpl<>(PaperRegistries.fromNms(named.key()), named);
|
||||
@@ -40,6 +66,30 @@ public final class PaperRegistrySets {
|
||||
}
|
||||
}
|
||||
|
||||
public static <A extends Keyed, M> RegistrySet<A> convertToApiWithDirects(final RegistryKey<A> registryKey, final HolderSet<M> holders) { // TODO remove Keyed
|
||||
if (holders instanceof final HolderSet.Named<M> named) {
|
||||
return new NamedRegistryKeySetImpl<>(PaperRegistries.fromNms(named.key()), named);
|
||||
} else {
|
||||
if (holders.size() == 0) {
|
||||
return RegistrySet.keySet(registryKey);
|
||||
}
|
||||
if (holders.get(0) instanceof Holder.Direct<M>) {
|
||||
final List<A> directs = new ArrayList<>(holders.size());
|
||||
final ResourceKey<? extends Registry<M>> nmsRegistryKey = PaperRegistries.registryToNms(registryKey);
|
||||
for (final Holder<M> holder : holders) {
|
||||
directs.add(CraftRegistry.minecraftHolderToBukkit(holder, nmsRegistryKey));
|
||||
}
|
||||
return RegistrySet.valueSet(registryKey, directs);
|
||||
} else {
|
||||
final List<TypedKey<A>> keys = new ArrayList<>(holders.size());
|
||||
for (final Holder<M> holder : holders) {
|
||||
keys.add(PaperRegistries.fromNms(((Holder.Reference<M>) holder).key()));
|
||||
}
|
||||
return RegistrySet.keySet(registryKey, keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PaperRegistrySets() {
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,33 @@
|
||||
package io.papermc.paper.registry.set;
|
||||
|
||||
import io.papermc.paper.registry.RegistryBuilder;
|
||||
import io.papermc.paper.registry.RegistryBuilderFactory;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.data.util.Conversions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import org.bukkit.Keyed;
|
||||
|
||||
public class RegistryValueSetBuilderImpl<M, API extends Keyed, BUILDER extends RegistryBuilder<API>> implements RegistryValueSetBuilder<API, BUILDER> { // TODO remove Keyed
|
||||
|
||||
private final RegistryKey<API> registryKey;
|
||||
private final Conversions conversions;
|
||||
private final List<API> instances = new ArrayList<>();
|
||||
|
||||
public RegistryValueSetBuilderImpl(final RegistryKey<API> registryKey, final Conversions conversions) {
|
||||
this.registryKey = registryKey;
|
||||
this.conversions = conversions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryValueSetBuilder<API, BUILDER> add(final Consumer<RegistryBuilderFactory<API, ? extends BUILDER>> builder) {
|
||||
this.instances.add(this.conversions.createApiInstanceFromBuilder(this.registryKey, builder));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryValueSet<API> build() {
|
||||
return RegistrySet.valueSet(this.registryKey, this.instances);
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
package io.papermc.paper.util;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.Decoder;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import io.papermc.paper.adventure.AdventureCodecs;
|
||||
import io.papermc.paper.registry.RegistryAccess;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import java.util.function.Function;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.RegistryOps;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Registry;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class PaperCodecs {
|
||||
|
||||
public static <A extends Keyed, M> Decoder<A> registryFileDecoderFor(final Decoder<? extends M> directNmsDecoder, final Function<? super Holder<M>, A> directHolderConverter, final RegistryKey<A> registryKey, final boolean allowInline) { // TODO remove Keyed
|
||||
final Decoder.Terminal<A> terminalDecoder = directNmsDecoder.map(nms -> directHolderConverter.apply(Holder.direct(nms))).terminal();
|
||||
return Decoder.ofTerminal(new Decoder.Terminal<>() {
|
||||
@Override
|
||||
public <T> DataResult<A> decode(final DynamicOps<T> ops, final T input) {
|
||||
// logic based on RegistryFileCodec
|
||||
if (ops instanceof RegistryOps<?>) {
|
||||
// Pretty sure we can just use our RegistryAccess here. These codecs aren't ever
|
||||
// used for deserialization, so we don't need to rely on different implementations
|
||||
// of HolderGetter like vanilla's RegistryFileCodec does. We are always just
|
||||
// getting existing dialog elements, never creating empty reference holders.
|
||||
final Registry<A> registry = RegistryAccess.registryAccess().getRegistry(registryKey);
|
||||
final DataResult<Pair<Key, T>> keyDataResult = AdventureCodecs.KEY_CODEC.decode(ops, input);
|
||||
if (keyDataResult.result().isEmpty()) {
|
||||
return !allowInline ? DataResult.error(() -> "Inline definitions not allowed here") : terminalDecoder.decode(ops, input);
|
||||
}
|
||||
final Pair<Key, T> pair = keyDataResult.result().get();
|
||||
final TypedKey<A> elementKey = TypedKey.create(registryKey, pair.getFirst());
|
||||
final A value = registry.get(elementKey);
|
||||
if (value == null) {
|
||||
return DataResult.error(() -> "Failed to get element " + elementKey);
|
||||
}
|
||||
return DataResult.success(value);
|
||||
} else {
|
||||
return terminalDecoder.decode(ops, input);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private PaperCodecs() {
|
||||
}
|
||||
}
|
@@ -25,7 +25,7 @@ public class CraftArt extends OldEnumHolderable<Art, PaintingVariant> implements
|
||||
}
|
||||
|
||||
public static Holder<PaintingVariant> bukkitToMinecraftHolder(Art bukkit) {
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.PAINTING_VARIANT);
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
|
||||
}
|
||||
|
||||
public CraftArt(Holder<PaintingVariant> paintingVariant) {
|
||||
|
@@ -21,7 +21,7 @@ public class CraftJukeboxSong extends HolderableBase<net.minecraft.world.item.Ju
|
||||
}
|
||||
|
||||
public static Holder<net.minecraft.world.item.JukeboxSong> bukkitToMinecraftHolder(JukeboxSong bukkit) {
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.JUKEBOX_SONG);
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
|
||||
}
|
||||
|
||||
public CraftJukeboxSong(final Holder<net.minecraft.world.item.JukeboxSong> holder) {
|
||||
|
@@ -28,7 +28,7 @@ public class CraftMusicInstrument extends MusicInstrument implements io.papermc.
|
||||
}
|
||||
|
||||
public static Holder<Instrument> bukkitToMinecraftHolder(MusicInstrument bukkit) {
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.INSTRUMENT);
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
|
||||
}
|
||||
|
||||
public static Object bukkitToString(MusicInstrument bukkit) {
|
||||
|
@@ -99,28 +99,18 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
|
||||
* @param bukkit the bukkit representation
|
||||
* @return the minecraft representation of the bukkit value
|
||||
*/
|
||||
public static <B extends Keyed, M> M bukkitToMinecraft(B bukkit) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <B extends Keyed, M> M bukkitToMinecraft(final B bukkit) {
|
||||
Preconditions.checkArgument(bukkit != null);
|
||||
|
||||
return ((Handleable<M>) bukkit).getHandle();
|
||||
}
|
||||
|
||||
public static <B extends Keyed, M> Holder<M> bukkitToMinecraftHolder(B bukkit, ResourceKey<net.minecraft.core.Registry<M>> registryKey) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <B extends Keyed, M> Holder<M> bukkitToMinecraftHolder(final B bukkit) {
|
||||
Preconditions.checkArgument(bukkit != null);
|
||||
// Paper start - support direct Holder
|
||||
if (bukkit instanceof io.papermc.paper.util.Holderable<?>) {
|
||||
return ((io.papermc.paper.util.Holderable<M>) bukkit).getHolder();
|
||||
}
|
||||
// Paper end - support direct Holder
|
||||
|
||||
net.minecraft.core.Registry<M> registry = CraftRegistry.getMinecraftRegistry(registryKey);
|
||||
|
||||
if (registry.wrapAsHolder(CraftRegistry.bukkitToMinecraft(bukkit)) instanceof Holder.Reference<M> holder) {
|
||||
return holder;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("No Reference holder found for " + bukkit
|
||||
+ ", this can happen if a plugin creates its own registry entry with out properly registering it.");
|
||||
return ((Holderable<M>) bukkit).getHolder();
|
||||
}
|
||||
|
||||
// Paper start - fixup upstream being dum
|
||||
|
@@ -23,7 +23,7 @@ public class CraftSound extends OldEnumHolderable<Sound, SoundEvent> implements
|
||||
}
|
||||
|
||||
public static Holder<SoundEvent> bukkitToMinecraftHolder(Sound bukkit) {
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.SOUND_EVENT);
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
|
||||
}
|
||||
|
||||
public CraftSound(Holder<SoundEvent> soundEffect) {
|
||||
|
@@ -43,7 +43,7 @@ public class CraftAttribute extends OldEnumHolderable<Attribute, net.minecraft.w
|
||||
}
|
||||
|
||||
public static Holder<net.minecraft.world.entity.ai.attributes.Attribute> bukkitToMinecraftHolder(Attribute bukkit) {
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.ATTRIBUTE);
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
|
||||
}
|
||||
|
||||
public static String bukkitToString(Attribute bukkit) {
|
||||
|
@@ -36,7 +36,7 @@ public class CraftBiome extends OldEnumHolderable<Biome, net.minecraft.world.lev
|
||||
if (bukkit == Biome.CUSTOM) {
|
||||
return null;
|
||||
}
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.BIOME);
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
|
||||
}
|
||||
|
||||
public CraftBiome(final Holder<net.minecraft.world.level.biome.Biome> holder) {
|
||||
|
@@ -24,7 +24,7 @@ public class CraftPatternType extends OldEnumHolderable<PatternType, BannerPatte
|
||||
}
|
||||
|
||||
public static Holder<BannerPattern> bukkitToMinecraftHolder(PatternType bukkit) {
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.BANNER_PATTERN);
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
|
||||
}
|
||||
|
||||
public CraftPatternType(Holder<BannerPattern> bannerPatternType) {
|
||||
|
@@ -79,7 +79,7 @@ public class CraftDamageType extends HolderableBase<net.minecraft.world.damageso
|
||||
}
|
||||
|
||||
public static Holder<net.minecraft.world.damagesource.DamageType> bukkitToMinecraftHolder(DamageType bukkitDamageType) {
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkitDamageType, Registries.DAMAGE_TYPE);
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkitDamageType);
|
||||
}
|
||||
|
||||
public static net.minecraft.world.damagesource.DamageType bukkitToMinecraft(DamageType bukkitDamageType) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user