From ea9b20ce59b8ac5da0ad1b7de98dce05895b7084 Mon Sep 17 00:00:00 2001 From: syldium Date: Wed, 20 Oct 2021 21:59:13 +0200 Subject: [PATCH] Add advancement display API (#6175) --- patches/api/Add-advancement-display-API.patch | 214 ++++++++++++++++++ .../server/Add-advancement-display-API.patch | 155 +++++++++++++ 2 files changed, 369 insertions(+) create mode 100644 patches/api/Add-advancement-display-API.patch create mode 100644 patches/server/Add-advancement-display-API.patch diff --git a/patches/api/Add-advancement-display-API.patch b/patches/api/Add-advancement-display-API.patch new file mode 100644 index 0000000000..d6c1d872a2 --- /dev/null +++ b/patches/api/Add-advancement-display-API.patch @@ -0,0 +1,214 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: syldium +Date: Fri, 9 Jul 2021 18:49:40 +0200 +Subject: [PATCH] Add advancement display API + + +diff --git a/src/main/java/io/papermc/paper/advancement/AdvancementDisplay.java b/src/main/java/io/papermc/paper/advancement/AdvancementDisplay.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/advancement/AdvancementDisplay.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.advancement; ++ ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.format.NamedTextColor; ++import net.kyori.adventure.text.format.TextColor; ++import net.kyori.adventure.translation.Translatable; ++import net.kyori.adventure.util.Index; ++import org.bukkit.NamespacedKey; ++import org.bukkit.inventory.ItemStack; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++/** ++ * Describes the display of an advancement. ++ *

++ * The display is used in the chat, in the toast messages and the advancements ++ * screen. ++ */ ++public interface AdvancementDisplay { ++ ++ /** ++ * Gets the {@link Frame}. ++ *

++ * This defines the appearance of the tile in the advancements screen and ++ * the text when it's completed. ++ * ++ * @return the frame type ++ */ ++ @NotNull ++ Frame frame(); ++ ++ /** ++ * Gets the advancement title. ++ * ++ * @return the title ++ */ ++ @NotNull ++ Component title(); ++ ++ /** ++ * Gets the description. ++ * ++ * @return the description ++ */ ++ @NotNull ++ Component description(); ++ ++ /** ++ * Gets the icon shown in the frame in the advancements screen. ++ * ++ * @return a copy of the icon ++ */ ++ @NotNull ++ ItemStack icon(); ++ ++ /** ++ * Gets whether a toast should be displayed. ++ *

++ * A toast is a notification that will be displayed in the top right corner ++ * of the screen. ++ * ++ * @return {@code true} if a toast should be shown ++ */ ++ boolean doesShowToast(); ++ ++ /** ++ * Gets whether a message should be sent in the chat. ++ * ++ * @return {@code true} if a message should be sent ++ * @see org.bukkit.event.player.PlayerAdvancementDoneEvent#message() to edit ++ * the message ++ */ ++ boolean doesAnnounceToChat(); ++ ++ /** ++ * Gets whether this advancement is hidden. ++ *

++ * Hidden advancements cannot be viewed by the player until they have been ++ * unlocked. ++ * ++ * @return {@code true} if hidden ++ */ ++ boolean isHidden(); ++ ++ /** ++ * Gets the texture displayed behind the advancement tree when selected. ++ *

++ * This only affects root advancements without any parent. If the background ++ * is not specified or doesn't exist, the tab background will be the missing ++ * texture. ++ * ++ * @return the background texture path ++ */ ++ @Nullable ++ NamespacedKey backgroundPath(); ++ ++ /** ++ * Defines how the {@link #icon()} appears in the advancements screen and ++ * the color used with the {@link #title() advancement name}. ++ */ ++ enum Frame implements Translatable { ++ ++ /** ++ * "Challenge complete" advancement. ++ *

++ * The client will play the {@code ui.toast.challenge_complete} sound ++ * when the challenge is completed and the toast is shown. ++ */ ++ CHALLENGE("challenge", NamedTextColor.DARK_PURPLE), ++ ++ /** ++ * "Goal reached" advancement. ++ */ ++ GOAL("goal", NamedTextColor.GREEN), ++ ++ /** ++ * "Advancement made" advancement. ++ */ ++ TASK("task", NamedTextColor.GREEN); ++ ++ /** ++ * The name map. ++ */ ++ public static final Index NAMES = Index.create(Frame.class, frame -> frame.name); ++ private final String name; ++ private final TextColor color; ++ ++ Frame(String name, TextColor color) { ++ this.name = name; ++ this.color = color; ++ } ++ ++ /** ++ * Gets the {@link TextColor} used for the advancement name. ++ * ++ * @return the text color ++ */ ++ @NotNull ++ public TextColor color() { ++ return this.color; ++ } ++ ++ /** ++ * Gets the translation key used when an advancement is completed. ++ *

++ * This is the first line of the toast displayed by the client. ++ * ++ * @return the toast message key ++ */ ++ @Override ++ @NotNull ++ public String translationKey() { ++ return "advancements.toast." + this.name; ++ } ++ } ++} +diff --git a/src/main/java/org/bukkit/advancement/Advancement.java b/src/main/java/org/bukkit/advancement/Advancement.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/advancement/Advancement.java ++++ b/src/main/java/org/bukkit/advancement/Advancement.java +@@ -0,0 +0,0 @@ public interface Advancement extends Keyed { + */ + @NotNull + Collection getCriteria(); ++ // Paper start ++ /** ++ * Get the display info of this advancement. ++ *

++ * Will be {@code null} when totally hidden, for example with crafting ++ * recipes. ++ * ++ * @return the display info ++ */ ++ @org.jetbrains.annotations.Nullable ++ io.papermc.paper.advancement.AdvancementDisplay getDisplay(); ++ ++ /** ++ * Gets the parent advancement, if any. ++ * ++ * @return the parent advancement ++ */ ++ @org.jetbrains.annotations.Nullable ++ Advancement getParent(); ++ ++ /** ++ * Gets all the direct children advancements. ++ * ++ * @return the children advancements ++ */ ++ @NotNull ++ @org.jetbrains.annotations.Unmodifiable ++ Collection getChildren(); ++ ++ /** ++ * Gets the root advancement of the tree this is located in. ++ * ++ * @return the root advancement ++ */ ++ @NotNull ++ Advancement getRoot(); ++ // Paper end + } diff --git a/patches/server/Add-advancement-display-API.patch b/patches/server/Add-advancement-display-API.patch new file mode 100644 index 0000000000..e6c4bd3548 --- /dev/null +++ b/patches/server/Add-advancement-display-API.patch @@ -0,0 +1,155 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: syldium +Date: Fri, 9 Jul 2021 18:50:40 +0200 +Subject: [PATCH] Add advancement display API + + +diff --git a/src/main/java/io/papermc/paper/advancement/PaperAdvancementDisplay.java b/src/main/java/io/papermc/paper/advancement/PaperAdvancementDisplay.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/advancement/PaperAdvancementDisplay.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.advancement; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import net.kyori.adventure.text.Component; ++import net.minecraft.advancements.DisplayInfo; ++import net.minecraft.advancements.FrameType; ++import org.bukkit.NamespacedKey; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.util.CraftNamespacedKey; ++import org.bukkit.inventory.ItemStack; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++public record PaperAdvancementDisplay(DisplayInfo handle) implements AdvancementDisplay { ++ ++ @Override ++ public @NotNull Frame frame() { ++ return asPaperFrame(this.handle.getFrame()); ++ } ++ ++ @Override ++ public @NotNull Component title() { ++ return PaperAdventure.asAdventure(this.handle.getTitle()); ++ } ++ ++ @Override ++ public @NotNull Component description() { ++ return PaperAdventure.asAdventure(this.handle.getDescription()); ++ } ++ ++ @Override ++ public @NotNull ItemStack icon() { ++ return CraftItemStack.asBukkitCopy(this.handle.getIcon()); ++ } ++ ++ @Override ++ public boolean doesShowToast() { ++ return this.handle.shouldShowToast(); ++ } ++ ++ @Override ++ public boolean doesAnnounceToChat() { ++ return this.handle.shouldAnnounceChat(); ++ } ++ ++ @Override ++ public boolean isHidden() { ++ return this.handle.isHidden(); ++ } ++ ++ @Override ++ public @Nullable NamespacedKey backgroundPath() { ++ return this.handle.getBackground() == null ? null : CraftNamespacedKey.fromMinecraft(this.handle.getBackground()); ++ } ++ ++ public static @NotNull Frame asPaperFrame(@NotNull FrameType frameType) { ++ return switch (frameType) { ++ case TASK -> Frame.TASK; ++ case CHALLENGE -> Frame.CHALLENGE; ++ case GOAL -> Frame.GOAL; ++ }; ++ } ++} +diff --git a/src/main/java/net/minecraft/advancements/DisplayInfo.java b/src/main/java/net/minecraft/advancements/DisplayInfo.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/advancements/DisplayInfo.java ++++ b/src/main/java/net/minecraft/advancements/DisplayInfo.java +@@ -0,0 +0,0 @@ public class DisplayInfo { + private final boolean hidden; + private float x; + private float y; ++ public final io.papermc.paper.advancement.AdvancementDisplay paper = new io.papermc.paper.advancement.PaperAdvancementDisplay(this); // Paper + + public DisplayInfo(ItemStack icon, Component title, Component description, @Nullable ResourceLocation background, FrameType frame, boolean showToast, boolean announceToChat, boolean hidden) { + this.title = title; +diff --git a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java ++++ b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java +@@ -0,0 +0,0 @@ public class CraftAdvancement implements org.bukkit.advancement.Advancement { + public Collection getCriteria() { + return Collections.unmodifiableCollection(this.handle.getCriteria().keySet()); + } ++ // Paper start ++ @Override ++ public io.papermc.paper.advancement.AdvancementDisplay getDisplay() { ++ return this.handle.getDisplay() == null ? null : this.handle.getDisplay().paper; ++ } ++ ++ @Override ++ public org.bukkit.advancement.Advancement getParent() { ++ return this.handle.getParent() == null ? null : this.handle.getParent().bukkit; ++ } ++ ++ @Override ++ public Collection getChildren() { ++ final var children = com.google.common.collect.ImmutableList.builder(); ++ for (Advancement advancement : this.handle.getChildren()) { ++ children.add(advancement.bukkit); ++ } ++ return children.build(); ++ } ++ ++ @Override ++ public org.bukkit.advancement.Advancement getRoot() { ++ Advancement advancement = this.handle; ++ while (advancement.getParent() != null) { ++ advancement = advancement.getParent(); ++ } ++ return advancement.bukkit; ++ } ++ // Paper end + } +diff --git a/src/test/java/io/papermc/paper/advancement/AdvancementFrameTest.java b/src/test/java/io/papermc/paper/advancement/AdvancementFrameTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/advancement/AdvancementFrameTest.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.advancement; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import net.kyori.adventure.text.format.TextColor; ++import net.minecraft.advancements.FrameType; ++import net.minecraft.network.chat.TranslatableComponent; ++import org.junit.Test; ++ ++import static org.junit.Assert.assertEquals; ++ ++public class AdvancementFrameTest { ++ ++ @Test ++ public void test() { ++ for (FrameType nmsFrameType : FrameType.values()) { ++ final TextColor expectedColor = PaperAdventure.asAdventure(nmsFrameType.getChatColor()); ++ final String expectedTranslationKey = ((TranslatableComponent) nmsFrameType.getDisplayName()).getKey(); ++ final var frame = PaperAdvancementDisplay.asPaperFrame(nmsFrameType); ++ assertEquals("The translation keys should be the same", expectedTranslationKey, frame.translationKey()); ++ assertEquals("The frame colors should be the same", expectedColor, frame.color()); ++ assertEquals(nmsFrameType.getName(), AdvancementDisplay.Frame.NAMES.key(frame)); ++ } ++ } ++}