diff --git a/api/src/main/java/net/kyori/adventure/text/event/ClickEvent.java b/api/src/main/java/net/kyori/adventure/text/event/ClickEvent.java index a5313e9b9..302e08222 100644 --- a/api/src/main/java/net/kyori/adventure/text/event/ClickEvent.java +++ b/api/src/main/java/net/kyori/adventure/text/event/ClickEvent.java @@ -33,6 +33,7 @@ import net.kyori.adventure.nbt.api.BinaryTagHolder; import net.kyori.adventure.text.format.StyleBuilderApplicable; import net.kyori.adventure.util.Index; +import org.jspecify.annotations.Nullable; import static java.util.Objects.requireNonNull; @@ -179,6 +180,17 @@ static ClickEvent showDialog(final DialogLike dialog) { return ClickEventImpl.create(Action.SHOW_DIALOG, Payload.dialog(dialog)); } + /** + * Creates a click event that sends a custom event to the server. + * + * @param key the key identifying the payload + * @return the click event + * @since 4.23.0 + */ + static ClickEvent custom(final Key key) { + return ClickEvent.custom(key, null); + } + /** * Creates a click event that sends a custom event to the server. * @@ -190,9 +202,8 @@ static ClickEvent showDialog(final DialogLike dialog) { * @return the click event * @since 4.23.0 */ - static ClickEvent custom(final Key key, final BinaryTagHolder nbt) { + static ClickEvent custom(final Key key, final @Nullable BinaryTagHolder nbt) { requireNonNull(key, "key"); - requireNonNull(nbt, "nbt"); return ClickEventImpl.create(Action.CUSTOM, Payload.custom(key, nbt)); } @@ -464,6 +475,17 @@ static ClickEvent.Payload.Dialog dialog(final DialogLike dialog) { return new PayloadImpl.DialogImpl(dialog); } + /** + * Creates a custom payload. + * + * @param key the key identifying the payload + * @return the payload + * @since 4.23.0 + */ + static ClickEvent.Payload.Custom custom(final Key key) { + return ClickEvent.Payload.custom(key, null); + } + /** * Creates a custom payload. * @@ -471,13 +493,12 @@ static ClickEvent.Payload.Dialog dialog(final DialogLike dialog) { * For simple use cases, you can use plain strings directly as SNBT.

* * @param key the key identifying the payload - * @param nbt the payload nbt data + * @param nbt the payload nbt data, optional * @return the payload * @since 4.23.0 */ - static ClickEvent.Payload.Custom custom(final Key key, final BinaryTagHolder nbt) { + static ClickEvent.Payload.Custom custom(final Key key, final @Nullable BinaryTagHolder nbt) { requireNonNull(key, "key"); - requireNonNull(nbt, "nbt"); return new PayloadImpl.CustomImpl(key, nbt); } @@ -528,21 +549,21 @@ sealed interface Dialog extends Payload permits PayloadImpl.DialogImpl { } /** - * A payload that holds custom data. + * A payload with a key and optional custom NBT data. * * @see Action#CUSTOM * @since 4.22.0 */ sealed interface Custom extends Payload, Keyed permits PayloadImpl.CustomImpl { /** - * The custom data. + * The optional custom data. * *

See {@link BinaryTagHolder#string()} for a simple way to return SNBT from NBT data.

* * @return the data * @since 4.23.0 */ - BinaryTagHolder nbt(); + @Nullable BinaryTagHolder nbt(); } } } diff --git a/api/src/main/java/net/kyori/adventure/text/event/PayloadImpl.java b/api/src/main/java/net/kyori/adventure/text/event/PayloadImpl.java index 0381dfeb6..d93b8748b 100644 --- a/api/src/main/java/net/kyori/adventure/text/event/PayloadImpl.java +++ b/api/src/main/java/net/kyori/adventure/text/event/PayloadImpl.java @@ -26,6 +26,7 @@ import net.kyori.adventure.dialog.DialogLike; import net.kyori.adventure.key.Key; import net.kyori.adventure.nbt.api.BinaryTagHolder; +import org.jspecify.annotations.Nullable; final class PayloadImpl { private PayloadImpl() { @@ -40,6 +41,6 @@ record IntImpl(int integer) implements ClickEvent.Payload.Int { record DialogImpl(DialogLike dialog) implements ClickEvent.Payload.Dialog { } - record CustomImpl(Key key, BinaryTagHolder nbt) implements ClickEvent.Payload.Custom { + record CustomImpl(Key key, @Nullable BinaryTagHolder nbt) implements ClickEvent.Payload.Custom { } } diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/ClickTag.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/ClickTag.java index 459c94308..9997019d7 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/ClickTag.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/ClickTag.java @@ -62,7 +62,10 @@ record ClickTag() { }, QuotingOverride.QUOTED); if (payload instanceof ClickEvent.Payload.Custom custom) { - emitter.argument(custom.nbt().string()); + final BinaryTagHolder nbt = custom.nbt(); + if (nbt != null) { + emitter.argument(nbt.string()); + } } }) ); @@ -90,9 +93,14 @@ static Tag create(final ArgumentQueue args, final Context ctx) throws ParsingExc throw ctx.newException("'custom' click event requires a valid key argument", ex, args); } - final String nbt = args.popOr("'custom' click event requires a nbt argument").value(); + final String nbt; + if (args.hasNext()) { + nbt = args.pop().value(); + } else { + nbt = null; + } - yield ClickEvent.custom(key, BinaryTagHolder.binaryTagHolder(nbt)); + yield ClickEvent.custom(key, nbt == null ? null : BinaryTagHolder.binaryTagHolder(nbt)); } case ClickEvent.Action.ShowDialog ignored -> throw ctx.newException("'show_dialog' click events are not supported in MiniMessage yet"); diff --git a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/StyleSerializer.java b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/StyleSerializer.java index f75e3c2bb..3852328db 100644 --- a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/StyleSerializer.java +++ b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/StyleSerializer.java @@ -227,7 +227,7 @@ public Style read(final JsonReader in) throws IOException { if (value != null) style.clickEvent(ClickEvent.copyToClipboard(value)); } case ClickEvent.Action.Custom ignored -> { - if (key != null && value != null) style.clickEvent(ClickEvent.custom(key, BinaryTagHolder.binaryTagHolder(value))); + if (key != null) style.clickEvent(ClickEvent.custom(key, value == null ? null : BinaryTagHolder.binaryTagHolder(value))); } // Not readable. case ClickEvent.Action.ShowDialog ignored -> { @@ -360,8 +360,12 @@ private void writeClickEvent(final ClickEvent clickEvent, final JsonWriter ou case ClickEvent.Payload.Custom customPayload -> { out.name(CLICK_EVENT_ID); this.gson.toJson(customPayload.key(), SerializerFactory.KEY_TYPE, out); - out.name(CLICK_EVENT_PAYLOAD); - out.value(customPayload.nbt().string()); + + final BinaryTagHolder nbt = customPayload.nbt(); + if (nbt != null) { + out.name(CLICK_EVENT_PAYLOAD); + out.value(nbt.string()); + } } case ClickEvent.Payload.Int intPayload -> { out.name(CLICK_EVENT_PAGE);