From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: syldium <syldium@mailo.com>
Date: Fri, 9 Jul 2021 18:50:40 +0200
Subject: [PATCH] Add more advancement API

== AT ==
public net.minecraft.advancements.Advancement decorateName(Lnet/minecraft/advancements/DisplayInfo;)Lnet/minecraft/network/chat/Component;

Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>

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.Advancement;
+import net.minecraft.advancements.AdvancementType;
+import net.minecraft.advancements.DisplayInfo;
+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.getType());
+    }
+
+    @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().map(CraftNamespacedKey::fromMinecraft).orElse(null);
+    }
+
+    @Override
+    public @NotNull Component displayName() {
+        return PaperAdventure.asAdventure(Advancement.decorateName(java.util.Objects.requireNonNull(this.handle, "cannot build display name for null handle, invalid state")));
+    }
+
+    public static @NotNull Frame asPaperFrame(final @NotNull AdvancementType 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 - Add more advancement API
 
     public DisplayInfo(
         ItemStack icon,
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 {
         return Collections.unmodifiableCollection(this.handle.value().criteria().keySet());
     }
 
+    // Paper start - Add more advancement API
     @Override
-    public AdvancementDisplay getDisplay() {
-        if (this.handle.value().display().isEmpty()) {
-            return null;
+    public io.papermc.paper.advancement.AdvancementDisplay getDisplay() {
+        return this.handle.value().display().map(d -> d.paper).orElse(null);
+    }
+
+    @Deprecated
+    @io.papermc.paper.annotation.DoNotUse
+    public AdvancementDisplay getDisplay0() { // May be called by plugins via Commodore
+        return this.handle.value().display().map(CraftAdvancementDisplay::new).orElse(null);
+    }
+
+    @Override
+    public net.kyori.adventure.text.Component displayName() {
+        return io.papermc.paper.adventure.PaperAdventure.asAdventure(net.minecraft.advancements.Advancement.name(this.handle));
+    }
+
+    @Override
+    public org.bukkit.advancement.Advancement getParent() {
+        return this.handle.value().parent()
+            .map(net.minecraft.server.MinecraftServer.getServer().getAdvancements()::get)
+            .map(AdvancementHolder::toBukkit)
+            .orElse(null);
+    }
+
+    @Override
+    public Collection<org.bukkit.advancement.Advancement> getChildren() {
+        final com.google.common.collect.ImmutableList.Builder<org.bukkit.advancement.Advancement> children = com.google.common.collect.ImmutableList.<org.bukkit.advancement.Advancement>builder();
+        final net.minecraft.advancements.AdvancementNode advancementNode = net.minecraft.server.MinecraftServer.getServer().getAdvancements().tree().get(this.handle);
+        if (advancementNode != null) {
+            for (final net.minecraft.advancements.AdvancementNode child : advancementNode.children()) {
+                children.add(child.holder().toBukkit());
+            }
         }
+        return children.build();
+    }
 
-        return new CraftAdvancementDisplay(this.handle.value().display().get());
+    @Override
+    public org.bukkit.advancement.Advancement getRoot() {
+        final net.minecraft.advancements.AdvancementNode advancementNode = net.minecraft.server.MinecraftServer.getServer().getAdvancements().tree().get(this.handle);
+        return java.util.Objects.requireNonNull(advancementNode, "could not find internal advancement node for advancement " + this.handle.id()).root().holder().toBukkit();
     }
+    // Paper end - Add more advancement API
 }
diff --git a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java
+++ b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java
@@ -0,0 +0,0 @@ import org.bukkit.craftbukkit.inventory.CraftItemStack;
 import org.bukkit.craftbukkit.util.CraftChatMessage;
 import org.bukkit.inventory.ItemStack;
 
+@Deprecated // Paper
 public class CraftAdvancementDisplay implements org.bukkit.advancement.AdvancementDisplay {
 
     private final DisplayInfo handle;
diff --git a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
@@ -0,0 +0,0 @@ public class Commodore {
                             super.visitMethodInsn(opcode, owner, name, "()Lcom/destroystokyo/paper/profile/PlayerProfile;", itf);
                             return;
                         }
+                        if (owner.equals("org/bukkit/advancement/Advancement") && name.equals("getDisplay") && desc.endsWith(")Lorg/bukkit/advancement/AdvancementDisplay;")) {
+                            super.visitTypeInsn(Opcodes.CHECKCAST, runtimeCbPkgPrefix() + "advancement/CraftAdvancement");
+                            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, runtimeCbPkgPrefix() + "advancement/CraftAdvancement", "getDisplay0", desc, false);
+                            return;
+                        }
                         // Paper end
 
                         if (modern) {
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.AdvancementType;
+import net.minecraft.network.chat.contents.TranslatableContents;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AdvancementFrameTest {
+
+    @Test
+    public void test() {
+        for (final AdvancementType advancementType : AdvancementType.values()) {
+            final TextColor expectedColor = PaperAdventure.asAdventure(advancementType.getChatColor());
+            final String expectedTranslationKey = ((TranslatableContents) advancementType.getDisplayName().getContents()).getKey();
+            final var frame = PaperAdvancementDisplay.asPaperFrame(advancementType);
+            assertEquals(expectedTranslationKey, frame.translationKey(), "The translation keys should be the same");
+            assertEquals(expectedColor, frame.color(), "The frame colors should be the same");
+            assertEquals(advancementType.getSerializedName(), AdvancementDisplay.Frame.NAMES.key(frame));
+        }
+    }
+}