From c24e93200289efba721c576376801a9f46c7bc76 Mon Sep 17 00:00:00 2001 From: zml Date: Sat, 9 Dec 2023 19:15:38 -0800 Subject: [PATCH] feat(api): Add a callback feature for resource pack application --- .../kyori/adventure/audience/Audience.java | 71 ++++++++++ .../audience/ForwardingAudience.java | 23 +++- .../resource/ResourcePackCallback.java | 68 ++++++++++ .../resource/ResourcePackStatus.java | 122 ++++++++++++++++++ 4 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 api/src/main/java/net/kyori/adventure/resource/ResourcePackCallback.java create mode 100644 api/src/main/java/net/kyori/adventure/resource/ResourcePackStatus.java diff --git a/api/src/main/java/net/kyori/adventure/audience/Audience.java b/api/src/main/java/net/kyori/adventure/audience/Audience.java index cd413d5df..7e33685c7 100644 --- a/api/src/main/java/net/kyori/adventure/audience/Audience.java +++ b/api/src/main/java/net/kyori/adventure/audience/Audience.java @@ -37,6 +37,7 @@ import net.kyori.adventure.identity.Identity; import net.kyori.adventure.inventory.Book; import net.kyori.adventure.pointer.Pointered; +import net.kyori.adventure.resource.ResourcePackCallback; import net.kyori.adventure.resource.ResourcePackRequest; import net.kyori.adventure.resource.ResourcePackRequestLike; import net.kyori.adventure.sound.Sound; @@ -742,6 +743,42 @@ default void setResourcePack(final @NotNull ResourcePackRequestLike request, fin default void setResourcePack(final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest@NotNull... others) { } + /** + * Sends resource pack requests to this audience, replacing any existing resource packs that might be present. + * + *

Multiple resource packs are only supported since 1.20.3. On older versions, only the first pack will be sent.

+ * + * @param cb a callback to be executed when resource pack events associated with this application are received + * @param request the first resource pack request + * @param others other requests + * @see ResourcePackRequest + * @since 4.15.0 + */ + @SuppressWarnings("checkstyle:MethodName") + @ForwardingAudienceOverrideNotRequired + default void setResourcePack(final @NotNull ResourcePackCallback cb, final @NotNull ResourcePackRequestLike request, final @NotNull ResourcePackRequestLike@NotNull... others) { + final ResourcePackRequest[] otherReqs = new ResourcePackRequest[others.length]; + for (int i = 0; i < others.length; i++) { + otherReqs[i] = others[i].asResourcePackRequest(); + } + this.setResourcePack(cb, request.asResourcePackRequest(), otherReqs); + } + + /** + * Sends resource pack requests to this audience, replacing any resource packs that might be present. + * + *

Multiple resource packs are only supported since 1.20.3. On older versions, only the first pack will be sent.

+ * + * @param cb a callback to be executed when resource pack events associated with this application are received + * @param request the resource pack request + * @param others other requests + * @see ResourcePackRequest + * @since 4.15.0 + */ + @SuppressWarnings("checkstyle:MethodName") + default void setResourcePack(final @NotNull ResourcePackCallback cb, final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest@NotNull... others) { + } + /** * Sends resource pack requests to this audience, adding to any existing resource packs that might be present. * @@ -774,6 +811,40 @@ default void sendResourcePack(final @NotNull ResourcePackRequestLike request, fi default void sendResourcePack(final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest@NotNull... others) { } + /** + * Sends resource pack requests to this audience, adding to any existing resource packs that might be present. + * + *

Multiple resource packs are only supported since 1.20.3. On older versions, this behaves identically to {@link #setResourcePack(ResourcePackRequestLike, ResourcePackRequestLike...)}.

+ * + * @param cb a callback to be executed when resource pack events associated with this application are received + * @param request the resource pack request + * @param others other requests + * @see ResourcePackRequest + * @since 4.15.0 + */ + @ForwardingAudienceOverrideNotRequired + default void sendResourcePack(final @NotNull ResourcePackCallback cb, final @NotNull ResourcePackRequestLike request, final @NotNull ResourcePackRequestLike@NotNull... others) { + final ResourcePackRequest[] otherReqs = new ResourcePackRequest[others.length]; + for (int i = 0; i < others.length; i++) { + otherReqs[i] = others[i].asResourcePackRequest(); + } + this.sendResourcePack(cb, request.asResourcePackRequest(), otherReqs); + } + + /** + * Sends resource pack requests to this audience, adding to any existing resource packs that might be present. + * + *

Multiple resource packs are only supported since 1.20.3. On older versions, this behaves identically to {@link #setResourcePack(ResourcePackRequest, ResourcePackRequest...)}.

+ * + * @param cb a callback to be executed when resource pack events associated with this application are received + * @param request the resource pack request + * @param others other requests + * @see ResourcePackRequest + * @since 4.15.0 + */ + default void sendResourcePack(final @NotNull ResourcePackCallback cb, final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest@NotNull... others) { + } + /** * Clear resource packs with the IDs used in the provided requests if they are present. * diff --git a/api/src/main/java/net/kyori/adventure/audience/ForwardingAudience.java b/api/src/main/java/net/kyori/adventure/audience/ForwardingAudience.java index c74353a7f..7d73eff51 100644 --- a/api/src/main/java/net/kyori/adventure/audience/ForwardingAudience.java +++ b/api/src/main/java/net/kyori/adventure/audience/ForwardingAudience.java @@ -39,6 +39,7 @@ import net.kyori.adventure.inventory.Book; import net.kyori.adventure.pointer.Pointer; import net.kyori.adventure.pointer.Pointers; +import net.kyori.adventure.resource.ResourcePackCallback; import net.kyori.adventure.resource.ResourcePackRequest; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.sound.SoundStop; @@ -202,15 +203,25 @@ default void openBook(final @NotNull Book book) { } @Override - default void setResourcePack(final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest... others) { + default void setResourcePack(final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest@NotNull... others) { for (final Audience audience : this.audiences()) audience.setResourcePack(request, others); } + @Override + default void setResourcePack(final @NotNull ResourcePackCallback cb, final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest @NotNull ... others) { + for (final Audience audience : this.audiences()) audience.setResourcePack(cb, request, others); + } + @Override default void sendResourcePack(final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest@NotNull... others) { for (final Audience audience : this.audiences()) audience.sendResourcePack(request, others); } + @Override + default void sendResourcePack(final @NotNull ResourcePackCallback cb, final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest@NotNull ... others) { + for (final Audience audience : this.audiences()) audience.sendResourcePack(cb, request, others); + } + @Override default void removeResourcePack(final @NotNull UUID id, final @NotNull UUID @NotNull ... others) { for (final Audience audience : this.audiences()) audience.removeResourcePack(id, others); @@ -389,11 +400,21 @@ default void setResourcePack(final @NotNull ResourcePackRequest request, final @ this.audience().setResourcePack(request, others); } + @Override + default void setResourcePack(final @NotNull ResourcePackCallback cb, final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest @NotNull ... others) { + this.audience().setResourcePack((uuid, status, audience) -> cb.packEventReceived(uuid, status, this), request, others); + } + @Override default void sendResourcePack(final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest@NotNull... others) { this.audience().sendResourcePack(request, others); } + @Override + default void sendResourcePack(final @NotNull ResourcePackCallback cb, final @NotNull ResourcePackRequest request, final @NotNull ResourcePackRequest @NotNull ... others) { + this.audience().sendResourcePack((uuid, status, audience) -> cb.packEventReceived(uuid, status, this), request, others); + } + @Override default void removeResourcePack(final @NotNull UUID id, final @NotNull UUID @NotNull ... others) { this.audience().removeResourcePack(id, others); diff --git a/api/src/main/java/net/kyori/adventure/resource/ResourcePackCallback.java b/api/src/main/java/net/kyori/adventure/resource/ResourcePackCallback.java new file mode 100644 index 000000000..d07db5882 --- /dev/null +++ b/api/src/main/java/net/kyori/adventure/resource/ResourcePackCallback.java @@ -0,0 +1,68 @@ +/* + * This file is part of adventure, licensed under the MIT License. + * + * Copyright (c) 2017-2023 KyoriPowered + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package net.kyori.adventure.resource; + +import java.util.UUID; +import java.util.function.BiConsumer; +import net.kyori.adventure.audience.Audience; +import org.jetbrains.annotations.NotNull; + +/** + * A callback for a resource pack application operation. + * + * @since 4.15.0 + */ +@FunctionalInterface +public interface ResourcePackCallback { + /** + * Create a pack callback that will only execute the provided functions when the pack application has completed, discarding all intermediate events. + * + * @param success the success callback + * @param failure the failure callback + * @return the created callback + * @since 4.15.0 + */ + static @NotNull ResourcePackCallback onTerminal(final @NotNull BiConsumer success, final @NotNull BiConsumer failure) { + return (uuid, status, audience) -> { + if (status == ResourcePackStatus.SUCCESSFULLY_LOADED) { + success.accept(uuid, audience); + } else if (!status.intermediate()) { + failure.accept(uuid, audience); + } + }; + } + + /** + * Called when a pack event has been received. + * + *

If the pack apply action was executed on a group audience, {@code audience} will referer to the + * individual member audiences the action is executed on. Forwarding audiences may wrap callbacks to ensure they receive the appropriate wrapped audience.

+ * + * @param uuid the uuid of the pack that has been applied. + * @param status the current pack status + * @param audience the audience the pack is being applied to + * @since 4.15.0 + */ + void packEventReceived(final @NotNull UUID uuid, final @NotNull ResourcePackStatus status, final @NotNull Audience audience); +} diff --git a/api/src/main/java/net/kyori/adventure/resource/ResourcePackStatus.java b/api/src/main/java/net/kyori/adventure/resource/ResourcePackStatus.java new file mode 100644 index 000000000..fe8ec8f8c --- /dev/null +++ b/api/src/main/java/net/kyori/adventure/resource/ResourcePackStatus.java @@ -0,0 +1,122 @@ +/* + * This file is part of adventure, licensed under the MIT License. + * + * Copyright (c) 2017-2023 KyoriPowered + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package net.kyori.adventure.resource; + +/** + * Resource pack application state. + * + *

Each status is a phase in the status state machine. + * The client provides this information back to servers as it attempts to download and apply resource packs.

+ * + *

Initial states are {@link #ACCEPTED}, {@link #DECLINED}, or {@link #INVALID_URL}.

+ * + * @since 4.15.0 + */ +public enum ResourcePackStatus { + /** + * Indicates that the user has accepted download. + * + *

Next states: {@link #FAILED_DOWNLOAD}, {@link #DOWNLOADED}

+ * + * @since 4.15.0 + */ + ACCEPTED(true), + /** + * Indicates that the user has declined a pack. + * + *

Terminal state.

+ * + * @since 4.15.0 + */ + DECLINED(false), + /** + * Indicates that the provided pack URL could not be parsed. + * + *

Terminal state.

+ * + * @since 4.15.0 + * @sinceMinecraft 1.20.3 + */ + INVALID_URL(false), + /** + * Indicates that the download failed for some other reason. + * + *

Terminal state.

+ * + * @since 4.15.0 + */ + FAILED_DOWNLOAD(false), + /** + * Indicates that the resource pack has been successfully downloaded. + * + *

Next states: {@link #FAILED_RELOAD}, {@link #DISCARDED}, or {@link #SUCCESSFULLY_LOADED}

+ * + * @since 4.15.0 + * @sinceMinecraft 1.20.3 + */ + DOWNLOADED(true), + /** + * Indicates that the client's resource manager reload failed. + * + *

Terminal state.

+ * + * @since 4.15.0 + * @sinceMinecraft 1.20.3 + */ + FAILED_RELOAD(false), + /** + * Indicates that this resource pack did not have issues, but was not applied due to a failure in another server resource pack. + * + *

Terminal state.

+ * + * @since 4.15.0 + * @sinceMinecraft 1.20.3 + */ + DISCARDED(false), + + /** + * Indicates that the pack has successfully loaded and resource reloading is complete. + * + *

Terminal state.

+ * + * @since 4.15.0 + */ + SUCCESSFULLY_LOADED(false); + + private final boolean intermediate; + + ResourcePackStatus(final boolean intermediate) { + this.intermediate = intermediate; + } + + /** + * Whether, after receiving this status, further status events might occur. + * + * @return the intermediate status + * @since 4.15.0 + */ + public boolean intermediate() { + return this.intermediate; + } +}