From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Riley Park <rileysebastianpark@gmail.com>
Date: Fri, 29 Jan 2021 17:21:55 +0100
Subject: [PATCH] Adventure

Co-authored-by: zml <zml@stellardrift.ca>
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
Co-authored-by: Yannick Lamprecht <yannicklamprecht@live.de>

diff --git a/build.gradle.kts b/build.gradle.kts
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -0,0 +0,0 @@ java {
 
 val annotationsVersion = "24.1.0"
 val bungeeCordChatVersion = "1.20-R0.2"
+val adventureVersion = "4.17.0"
+val apiAndDocs: Configuration by configurations.creating {
+    attributes {
+        attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
+        attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
+        attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType.SOURCES))
+        attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
+    }
+}
+configurations.api {
+    extendsFrom(apiAndDocs)
+}
 
 // Paper start - configure mockito agent that is needed in newer java versions
 val mockitoAgent = configurations.register("mockitoAgent")
@@ -0,0 +0,0 @@ dependencies {
     // api dependencies are listed transitively to API consumers
     api("com.google.guava:guava:32.1.2-jre")
     api("com.google.code.gson:gson:2.10.1")
-    api("net.md-5:bungeecord-chat:$bungeeCordChatVersion")
+    // Paper start - adventure
+    api("net.md-5:bungeecord-chat:$bungeeCordChatVersion-deprecated+build.18") {
+        exclude("com.google.guava", "guava")
+    }
+    // Paper - adventure
     api("org.yaml:snakeyaml:2.2")
     api("org.joml:joml:1.10.5")
     // Paper start
@@ -0,0 +0,0 @@ dependencies {
         isTransitive = false // includes junit
     }
     api("it.unimi.dsi:fastutil:8.5.6")
+    apiAndDocs(platform("net.kyori:adventure-bom:$adventureVersion"))
+    apiAndDocs("net.kyori:adventure-api")
+    apiAndDocs("net.kyori:adventure-text-minimessage")
+    apiAndDocs("net.kyori:adventure-text-serializer-gson")
+    apiAndDocs("net.kyori:adventure-text-serializer-legacy")
+    apiAndDocs("net.kyori:adventure-text-serializer-plain")
+    apiAndDocs("net.kyori:adventure-text-logger-slf4j")
     // Paper end
 
     compileOnly("org.apache.maven:maven-resolver-provider:3.9.6")
@@ -0,0 +0,0 @@ tasks.withType<Javadoc> {
         "https://guava.dev/releases/32.1.2-jre/api/docs/",
         "https://javadoc.io/doc/org.yaml/snakeyaml/2.2/",
         "https://javadoc.io/doc/org.jetbrains/annotations/$annotationsVersion/", // Paper - we don't want Java 5 annotations
-        "https://javadoc.io/doc/net.md-5/bungeecord-chat/$bungeeCordChatVersion/",
+        // "https://javadoc.io/doc/net.md-5/bungeecord-chat/$bungeeCordChatVersion/", // Paper - don't link to bungee chat
         // Paper start - add missing javadoc links
         "https://javadoc.io/doc/org.joml/joml/1.10.5/index.html",
         "https://www.javadoc.io/doc/com.google.code.gson/gson/2.10.1",
         "https://jspecify.dev/docs/api/",
         // Paper end
+        // Paper start
+        "https://jd.advntr.dev/api/$adventureVersion/",
+        "https://jd.advntr.dev/key/$adventureVersion/",
+        "https://jd.advntr.dev/text-minimessage/$adventureVersion/",
+        "https://jd.advntr.dev/text-serializer-gson/$adventureVersion/",
+        "https://jd.advntr.dev/text-serializer-legacy/$adventureVersion/",
+        "https://jd.advntr.dev/text-serializer-plain/$adventureVersion/",
+        "https://jd.advntr.dev/text-logger-slf4j/$adventureVersion/",
+        // Paper end
     )
     options.tags("apiNote:a:API Note:")
 
+    inputs.files(apiAndDocs).ignoreEmptyDirectories().withPropertyName(apiAndDocs.name + "-configuration")
+    doFirst {
+        options.addStringOption(
+            "sourcepath",
+            apiAndDocs.resolvedConfiguration.files.joinToString(separator = File.pathSeparator, transform = File::getPath)
+        )
+    }
+
     // workaround for https://github.com/gradle/gradle/issues/4046
     inputs.dir("src/main/javadoc").withPropertyName("javadoc-sourceset")
     doLast {
diff --git a/src/main/java/io/papermc/paper/chat/ChatRenderer.java b/src/main/java/io/papermc/paper/chat/ChatRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/chat/ChatRenderer.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.chat;
+
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A chat renderer is responsible for rendering chat messages sent by {@link Player}s to the server.
+ */
+@NullMarked
+@FunctionalInterface
+public interface ChatRenderer {
+
+    /**
+     * Renders a chat message. This will be called once for each receiving {@link Audience}.
+     *
+     * @param source the message source
+     * @param sourceDisplayName the display name of the source player
+     * @param message the chat message
+     * @param viewer the receiving {@link Audience}
+     * @return a rendered chat message
+     */
+    @ApiStatus.OverrideOnly
+    Component render(Player source, Component sourceDisplayName, Component message, Audience viewer);
+
+    /**
+     * Create a new instance of the default {@link ChatRenderer}.
+     *
+     * @return a new {@link ChatRenderer}
+     */
+    static ChatRenderer defaultRenderer() {
+        return new ViewerUnawareImpl.Default((source, sourceDisplayName, message) -> Component.translatable("chat.type.text", sourceDisplayName, message));
+    }
+
+    @ApiStatus.Internal
+    sealed interface Default extends ChatRenderer, ViewerUnaware permits ViewerUnawareImpl.Default {
+    }
+
+    /**
+     * Creates a new viewer-unaware {@link ChatRenderer}, which will render the chat message a single time,
+     * displaying the same rendered message to every viewing {@link Audience}.
+     *
+     * @param renderer the viewer unaware renderer
+     * @return a new {@link ChatRenderer}
+     */
+    static ChatRenderer viewerUnaware(final ViewerUnaware renderer) {
+        return new ViewerUnawareImpl(renderer);
+    }
+
+    /**
+     * Similar to {@link ChatRenderer}, but without knowledge of the message viewer.
+     *
+     * @see ChatRenderer#viewerUnaware(ViewerUnaware)
+     */
+    interface ViewerUnaware {
+
+        /**
+         * Renders a chat message.
+         *
+         * @param source the message source
+         * @param sourceDisplayName the display name of the source player
+         * @param message the chat message
+         * @return a rendered chat message
+         */
+        @ApiStatus.OverrideOnly
+        Component render(Player source, Component sourceDisplayName, Component message);
+    }
+}
diff --git a/src/main/java/io/papermc/paper/chat/ViewerUnawareImpl.java b/src/main/java/io/papermc/paper/chat/ViewerUnawareImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/chat/ViewerUnawareImpl.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.chat;
+
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@ApiStatus.Internal
+@NullMarked
+sealed class ViewerUnawareImpl implements ChatRenderer, ChatRenderer.ViewerUnaware permits ViewerUnawareImpl.Default {
+    private final ViewerUnaware unaware;
+    private @Nullable Component message;
+
+    ViewerUnawareImpl(final ViewerUnaware unaware) {
+        this.unaware = unaware;
+    }
+
+    @Override
+    public Component render(final Player source, final Component sourceDisplayName, final Component message, final Audience viewer) {
+        return this.render(source, sourceDisplayName, message);
+    }
+
+    @Override
+    public Component render(final Player source, final Component sourceDisplayName, final Component message) {
+        if (this.message == null) {
+            this.message = this.unaware.render(source, sourceDisplayName, message);
+        }
+        return this.message;
+    }
+
+    static final class Default extends ViewerUnawareImpl implements ChatRenderer.Default {
+        Default(final ViewerUnaware unaware) {
+            super(unaware);
+        }
+    }
+}
diff --git a/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java b/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.event.player;
+
+import io.papermc.paper.chat.ChatRenderer;
+import java.util.Set;
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.chat.SignedMessage;
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.player.PlayerEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * An abstract implementation of a chat event, handling shared logic.
+ */
+@ApiStatus.NonExtendable
+@NullMarked
+public abstract class AbstractChatEvent extends PlayerEvent implements Cancellable {
+
+    private final Set<Audience> viewers;
+    private final Component originalMessage;
+    private final SignedMessage signedMessage;
+    private ChatRenderer renderer;
+    private Component message;
+
+    private boolean cancelled;
+
+    AbstractChatEvent(final boolean async, final Player player, final Set<Audience> viewers, final ChatRenderer renderer, final Component message, final Component originalMessage, final SignedMessage signedMessage) {
+        super(player, async);
+        this.viewers = viewers;
+        this.renderer = renderer;
+        this.message = message;
+        this.originalMessage = originalMessage;
+        this.signedMessage = signedMessage;
+    }
+
+    /**
+     * Gets a set of {@link Audience audiences} that this chat message will be displayed to.
+     *
+     * <p>The set returned may auto-populate on access. Any listener accessing the returned set should be aware that
+     * it may reduce performance for a lazy set implementation.</p>
+     *
+     * @return a mutable set of {@link Audience audiences} who will receive the chat message
+     */
+    public final Set<Audience> viewers() {
+        return this.viewers;
+    }
+
+    /**
+     * Sets the chat renderer.
+     *
+     * @param renderer the chat renderer
+     * @throws NullPointerException if {@code renderer} is {@code null}
+     */
+    public final void renderer(final ChatRenderer renderer) {
+        this.renderer = requireNonNull(renderer, "renderer");
+    }
+
+    /**
+     * Gets the chat renderer.
+     *
+     * @return the chat renderer
+     */
+    public final ChatRenderer renderer() {
+        return this.renderer;
+    }
+
+    /**
+     * Gets the user-supplied message.
+     * The return value will reflect changes made using {@link #message(Component)}.
+     *
+     * @return the user-supplied message
+     */
+    public final Component message() {
+        return this.message;
+    }
+
+    /**
+     * Sets the user-supplied message.
+     *
+     * @param message the user-supplied message
+     * @throws NullPointerException if {@code message} is {@code null}
+     */
+    public final void message(final Component message) {
+        this.message = requireNonNull(message, "message");
+    }
+
+    /**
+     * Gets the original and unmodified user-supplied message.
+     * The return value will <b>not</b> reflect changes made using
+     * {@link #message(Component)}.
+     *
+     * @return the original user-supplied message
+     */
+    public final Component originalMessage() {
+        return this.originalMessage;
+    }
+
+    /**
+     * Gets the signed message.
+     * Changes made in this event will <b>not</b> update
+     * the signed message.
+     *
+     * @return the signed message
+     */
+    public final SignedMessage signedMessage() {
+        return this.signedMessage;
+    }
+
+    @Override
+    public final boolean isCancelled() {
+        return this.cancelled;
+    }
+
+    @Override
+    public final void setCancelled(final boolean cancel) {
+        this.cancelled = cancel;
+    }
+}
diff --git a/src/main/java/io/papermc/paper/event/player/AsyncChatCommandDecorateEvent.java b/src/main/java/io/papermc/paper/event/player/AsyncChatCommandDecorateEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/AsyncChatCommandDecorateEvent.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.event.player;
+
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@ApiStatus.Experimental
+@NullMarked
+public class AsyncChatCommandDecorateEvent extends AsyncChatDecorateEvent {
+
+    private static final HandlerList HANDLER_LIST = new HandlerList();
+
+    @ApiStatus.Internal
+    public AsyncChatCommandDecorateEvent(final @Nullable Player player, final Component originalMessage) {
+        super(player, originalMessage);
+    }
+
+    @Override
+    public HandlerList getHandlers() {
+        return HANDLER_LIST;
+    }
+
+    public static HandlerList getHandlerList() {
+        return HANDLER_LIST;
+    }
+}
diff --git a/src/main/java/io/papermc/paper/event/player/AsyncChatDecorateEvent.java b/src/main/java/io/papermc/paper/event/player/AsyncChatDecorateEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/AsyncChatDecorateEvent.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.event.player;
+
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.server.ServerEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * This event is fired when the server decorates a component for chat purposes. This is called
+ * before {@link AsyncChatEvent} and the other chat events. It is recommended that you modify the
+ * message here, and use the chat events for modifying receivers and later the chat type. If you
+ * want to keep the message as "signed" for the clients who get it, be sure to include the entire
+ * original message somewhere in the final message.
+ * <br>
+ * See {@link AsyncChatCommandDecorateEvent} for the decoration of messages sent via commands
+ */
+@ApiStatus.Experimental
+@NullMarked
+public class AsyncChatDecorateEvent extends ServerEvent implements Cancellable {
+
+    private static final HandlerList HANDLER_LIST = new HandlerList();
+
+    private final @Nullable Player player;
+    private final Component originalMessage;
+    private Component result;
+
+    private boolean cancelled;
+
+    @ApiStatus.Internal
+    public AsyncChatDecorateEvent(final @Nullable Player player, final Component originalMessage) {
+        super(true);
+        this.player = player;
+        this.originalMessage = originalMessage;
+        this.result = originalMessage;
+    }
+
+    /**
+     * Gets the player (if available) associated with this event.
+     * <p>
+     * Certain commands request decorations without a player context
+     * which is why this is possibly null.
+     *
+     * @return the player or {@code null}
+     */
+    public @Nullable Player player() {
+        return this.player;
+    }
+
+    /**
+     * Gets the original decoration input
+     *
+     * @return the input
+     */
+    public Component originalMessage() {
+        return this.originalMessage;
+    }
+
+    /**
+     * Gets the decoration result. This may already be different from
+     * {@link #originalMessage()} if some other listener to this event
+     * changed the result.
+     *
+     * @return the result
+     */
+    public Component result() {
+        return this.result;
+    }
+
+    /**
+     * Sets the resulting decorated component.
+     *
+     * @param result the result
+     */
+    public void result(final Component result) {
+        this.result = result;
+    }
+
+    @Override
+    public boolean isCancelled() {
+        return this.cancelled;
+    }
+
+    /**
+     * A cancelled decorating event means that no changes to the result component
+     * will have any effect. The decorated component will be equal to the original
+     * component.
+     */
+    @Override
+    public void setCancelled(final boolean cancel) {
+        this.cancelled = cancel;
+    }
+
+    @Override
+    public HandlerList getHandlers() {
+        return HANDLER_LIST;
+    }
+
+    public static HandlerList getHandlerList() {
+        return HANDLER_LIST;
+    }
+}
diff --git a/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java b/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.event.player;
+
+import io.papermc.paper.chat.ChatRenderer;
+import java.util.Set;
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.chat.SignedMessage;
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * An event fired when a {@link Player} sends a chat message to the server.
+ * <p>
+ * This event will sometimes fire synchronously, depending on how it was
+ * triggered.
+ * <p>
+ * If a player is the direct cause of this event by an incoming packet, this
+ * event will be asynchronous. If a plugin triggers this event by compelling a
+ * player to chat, this event will be synchronous.
+ * <p>
+ * Care should be taken to check {@link #isAsynchronous()} and treat the event
+ * appropriately.
+ */
+@NullMarked
+public final class AsyncChatEvent extends AbstractChatEvent {
+
+    private static final HandlerList HANDLER_LIST = new HandlerList();
+
+    @ApiStatus.Internal
+    public AsyncChatEvent(final boolean async, final Player player, final Set<Audience> viewers, final ChatRenderer renderer, final Component message, final Component originalMessage, final SignedMessage signedMessage) {
+        super(async, player, viewers, renderer, message, originalMessage, signedMessage);
+    }
+
+    @Override
+    public HandlerList getHandlers() {
+        return HANDLER_LIST;
+    }
+
+    public static HandlerList getHandlerList() {
+        return HANDLER_LIST;
+    }
+}
diff --git a/src/main/java/io/papermc/paper/event/player/ChatEvent.java b/src/main/java/io/papermc/paper/event/player/ChatEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/ChatEvent.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.event.player;
+
+import io.papermc.paper.chat.ChatRenderer;
+import java.util.Set;
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.chat.SignedMessage;
+import net.kyori.adventure.text.Component;
+import org.bukkit.Warning;
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * An event fired when a {@link Player} sends a chat message to the server.
+ *
+ * @deprecated Listening to this event forces chat to wait for the main thread, delaying chat messages.
+ * It is recommended to use {@link AsyncChatEvent} instead, wherever possible.
+ */
+@Deprecated
+@Warning(reason = "Listening to this event forces chat to wait for the main thread, delaying chat messages.")
+@NullMarked
+public final class ChatEvent extends AbstractChatEvent {
+
+    private static final HandlerList HANDLER_LIST = new HandlerList();
+
+    @ApiStatus.Internal
+    public ChatEvent(final Player player, final Set<Audience> viewers, final ChatRenderer renderer, final Component message, final Component originalMessage, final SignedMessage signedMessage) {
+        super(false, player, viewers, renderer, message, originalMessage, signedMessage);
+    }
+
+    @Override
+    public HandlerList getHandlers() {
+        return HANDLER_LIST;
+    }
+
+    public static HandlerList getHandlerList() {
+        return HANDLER_LIST;
+    }
+}
diff --git a/src/main/java/io/papermc/paper/text/PaperComponents.java b/src/main/java/io/papermc/paper/text/PaperComponents.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/text/PaperComponents.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.text;
+
+import java.io.IOException;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.flattener.ComponentFlattener;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
+import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Entity;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Paper API-specific methods for working with {@link Component}s and related.
+ */
+@NullMarked
+public final class PaperComponents {
+
+    private PaperComponents() {
+        throw new RuntimeException("PaperComponents is not to be instantiated!");
+    }
+
+    /**
+     * Resolves a component with a specific command sender and subject.
+     * <p>
+     * Note that in Vanilla, elevated permissions are usually required to use
+     * '@' selectors in various component types, but this method should not
+     * check such permissions from the sender.
+     * <p>
+     * A {@link CommandSender} argument is required to resolve:
+     * <ul>
+     *     <li>{@link net.kyori.adventure.text.NBTComponent}</li>
+     *     <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+     *     <li>{@link net.kyori.adventure.text.SelectorComponent}</li>
+     * </ul>
+     * A {@link Entity} argument is optional to help resolve:
+     * <ul>
+     *     <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+     * </ul>
+     * {@link net.kyori.adventure.text.TranslatableComponent}s don't require any extra arguments.
+     *
+     * @param input the component to resolve
+     * @param context the command sender to resolve with
+     * @param scoreboardSubject the scoreboard subject to use (for use with {@link net.kyori.adventure.text.ScoreComponent}s)
+     * @return the resolved component
+     * @throws IOException if a syntax error tripped during resolving
+     */
+    public static Component resolveWithContext(final Component input, final @Nullable CommandSender context, final @Nullable Entity scoreboardSubject) throws IOException {
+        return resolveWithContext(input, context, scoreboardSubject, true);
+    }
+
+    /**
+     * Resolves a component with a specific command sender and subject.
+     * <p>
+     * Note that in Vanilla, elevated permissions are required to use
+     * '@' selectors in various component types. If the boolean {@code bypassPermissions}
+     * argument is {@code false}, the {@link CommandSender} argument will be used to query
+     * those permissions.
+     * <p>
+     * A {@link CommandSender} argument is required to resolve:
+     * <ul>
+     *     <li>{@link net.kyori.adventure.text.NBTComponent}</li>
+     *     <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+     *     <li>{@link net.kyori.adventure.text.SelectorComponent}</li>
+     * </ul>
+     * A {@link Entity} argument is optional to help resolve:
+     * <ul>
+     *     <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+     * </ul>
+     * {@link net.kyori.adventure.text.TranslatableComponent}s don't require any extra arguments.
+     *
+     * @param input the component to resolve
+     * @param context the command sender to resolve with
+     * @param scoreboardSubject the scoreboard subject to use (for use with {@link net.kyori.adventure.text.ScoreComponent}s)
+     * @param bypassPermissions true to bypass permissions checks for resolving components
+     * @return the resolved component
+     * @throws IOException if a syntax error tripped during resolving
+     */
+    @SuppressWarnings("deprecation") // using unsafe as a bridge
+    public static Component resolveWithContext(final Component input, final @Nullable CommandSender context, final @Nullable Entity scoreboardSubject, final boolean bypassPermissions) throws IOException {
+        return Bukkit.getUnsafe().resolveWithContext(input, context, scoreboardSubject, bypassPermissions);
+    }
+
+    /**
+     * Return a component flattener that can use game data to resolve extra information about components.
+     *
+     * @return a component flattener
+     */
+    @SuppressWarnings("deprecation") // using unsafe as a bridge
+    public static ComponentFlattener flattener() {
+        return Bukkit.getUnsafe().componentFlattener();
+    }
+
+    /**
+     * Get a serializer for {@link Component}s that will convert components to
+     * a plain-text string.
+     *
+     * <p>Implementations may provide a serializer capable of processing any
+     * information that requires access to implementation details.</p>
+     *
+     * @return a serializer to plain text
+     * @deprecated will be removed in adventure 5.0.0, use {@link PlainTextComponentSerializer#plainText()}
+     */
+    @Deprecated(forRemoval = true, since = "1.18.1")
+    public static PlainComponentSerializer plainSerializer() {
+        return Bukkit.getUnsafe().plainComponentSerializer();
+    }
+
+    /**
+     * Get a serializer for {@link Component}s that will convert components to
+     * a plain-text string.
+     *
+     * <p>Implementations may provide a serializer capable of processing any
+     * information that requires access to implementation details.</p>
+     *
+     * @return a serializer to plain text
+     * @deprecated use {@link PlainTextComponentSerializer#plainText()}
+     */
+    @Deprecated(forRemoval = true, since = "1.18.2")
+    public static PlainTextComponentSerializer plainTextSerializer() {
+        return Bukkit.getUnsafe().plainTextSerializer();
+    }
+
+    /**
+     * Get a serializer for {@link Component}s that will convert to and from the
+     * standard JSON serialization format using Gson.
+     *
+     * <p>Implementations may provide a serializer capable of processing any
+     * information that requires implementation details, such as legacy
+     * (pre-1.16) hover events.</p>
+     *
+     * @return a json component serializer
+     * @deprecated use {@link GsonComponentSerializer#gson()}
+     */
+    @Deprecated(forRemoval = true, since = "1.18.2")
+    public static GsonComponentSerializer gsonSerializer() {
+        return Bukkit.getUnsafe().gsonComponentSerializer();
+    }
+
+    /**
+     * Get a serializer for {@link Component}s that will convert to and from the
+     * standard JSON serialization format using Gson, downsampling any RGB colors
+     * to their nearest {@link NamedTextColor} counterpart.
+     *
+     * <p>Implementations may provide a serializer capable of processing any
+     * information that requires implementation details, such as legacy
+     * (pre-1.16) hover events.</p>
+     *
+     * @return a json component serializer
+     * @deprecated use {@link GsonComponentSerializer#colorDownsamplingGson()}
+     */
+    @Deprecated(forRemoval = true, since = "1.18.2")
+    public static GsonComponentSerializer colorDownsamplingGsonSerializer() {
+        return Bukkit.getUnsafe().colorDownsamplingGsonComponentSerializer();
+    }
+
+    /**
+     * Get a serializer for {@link Component}s that will convert to and from the
+     * legacy component format used by Bukkit. This serializer uses the
+     * {@link LegacyComponentSerializer.Builder#useUnusualXRepeatedCharacterHexFormat()}
+     * option to match upstream behavior.
+     *
+     * <p>This legacy serializer uses the standard section symbol to mark
+     * formatting characters.</p>
+     *
+     * <p>Implementations may provide a serializer capable of processing any
+     * information that requires access to implementation details.</p>
+     *
+     * @return a section serializer
+     * @deprecated use {@link LegacyComponentSerializer#legacySection()}
+     */
+    @Deprecated(forRemoval = true, since = "1.18.2")
+    public static LegacyComponentSerializer legacySectionSerializer() {
+        return Bukkit.getUnsafe().legacyComponentSerializer();
+    }
+}
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -0,0 +0,0 @@ public final class Bukkit {
      *
      * @param message the message
      * @return the number of players
+     * @deprecated in favour of {@link Server#broadcast(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public static int broadcastMessage(@NotNull String message) {
         return server.broadcastMessage(message);
     }
@@ -0,0 +0,0 @@ public final class Bukkit {
         server.shutdown();
     }
 
+    // Paper start
+    /**
+     * Broadcast a message to all players.
+     * <p>
+     * This is the same as calling {@link #broadcast(net.kyori.adventure.text.Component,
+     * java.lang.String)} with the {@link Server#BROADCAST_CHANNEL_USERS} permission.
+     *
+     * @param message the message
+     * @return the number of players
+     */
+    public static int broadcast(net.kyori.adventure.text.@NotNull Component message) {
+        return server.broadcast(message);
+    }
     /**
      * Broadcasts the specified message to every user with the given
      * permission name.
@@ -0,0 +0,0 @@ public final class Bukkit {
      *     permissibles} must have to receive the broadcast
      * @return number of message recipients
      */
+    public static int broadcast(net.kyori.adventure.text.@NotNull Component message, @NotNull String permission) {
+        return server.broadcast(message, permission);
+    }
+    // Paper end
+    /**
+     * Broadcasts the specified message to every user with the given
+     * permission name.
+     *
+     * @param message message to broadcast
+     * @param permission the required permission {@link Permissible
+     *     permissibles} must have to receive the broadcast
+     * @return number of message recipients
+     * @deprecated in favour of {@link #broadcast(net.kyori.adventure.text.Component, String)}
+     */
+    @Deprecated // Paper
     public static int broadcast(@NotNull String message, @NotNull String permission) {
         return server.broadcast(message, permission);
     }
@@ -0,0 +0,0 @@ public final class Bukkit {
         return server.createInventory(owner, type);
     }
 
+    // Paper start
     /**
      * Creates an empty inventory with the specified type and title. If the type
      * is {@link InventoryType#CHEST}, the new inventory has a size of 27;
@@ -0,0 +0,0 @@ public final class Bukkit {
      * @see InventoryType#isCreatable()
      */
     @NotNull
+    public static Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type, net.kyori.adventure.text.@NotNull Component title) {
+        return server.createInventory(owner, type, title);
+    }
+    // Paper end
+
+    /**
+     * Creates an empty inventory with the specified type and title. If the type
+     * is {@link InventoryType#CHEST}, the new inventory has a size of 27;
+     * otherwise the new inventory has the normal size for its type.<br>
+     * It should be noted that some inventory types do not support titles and
+     * may not render with said titles on the Minecraft client.
+     * <br>
+     * {@link InventoryType#WORKBENCH} will not process crafting recipes if
+     * created with this method. Use
+     * {@link Player#openWorkbench(Location, boolean)} instead.
+     * <br>
+     * {@link InventoryType#ENCHANTING} will not process {@link ItemStack}s
+     * for possible enchanting results. Use
+     * {@link Player#openEnchanting(Location, boolean)} instead.
+     *
+     * @param owner The holder of the inventory; can be null if there's no holder.
+     * @param type The type of inventory to create.
+     * @param title The title of the inventory, to be displayed when it is viewed.
+     * @return The new inventory.
+     * @throws IllegalArgumentException if the {@link InventoryType} cannot be
+     * viewed.
+     * @deprecated in favour of {@link #createInventory(InventoryHolder, InventoryType, net.kyori.adventure.text.Component)}
+     *
+     * @see InventoryType#isCreatable()
+     */
+    @Deprecated // Paper
+    @NotNull
     public static Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type, @NotNull String title) {
         return server.createInventory(owner, type, title);
     }
@@ -0,0 +0,0 @@ public final class Bukkit {
         return server.createInventory(owner, size);
     }
 
+    // Paper start
     /**
      * Creates an empty inventory of type {@link InventoryType#CHEST} with the
      * specified size and title.
@@ -0,0 +0,0 @@ public final class Bukkit {
      * @throws IllegalArgumentException if the size is not a multiple of 9
      */
     @NotNull
+    public static Inventory createInventory(@Nullable InventoryHolder owner, int size, net.kyori.adventure.text.@NotNull Component title) throws IllegalArgumentException {
+        return server.createInventory(owner, size, title);
+    }
+    // Paper end
+
+    /**
+     * Creates an empty inventory of type {@link InventoryType#CHEST} with the
+     * specified size and title.
+     *
+     * @param owner the holder of the inventory, or null to indicate no holder
+     * @param size a multiple of 9 as the size of inventory to create
+     * @param title the title of the inventory, displayed when inventory is
+     *     viewed
+     * @return a new inventory
+     * @throws IllegalArgumentException if the size is not a multiple of 9
+     * @deprecated in favour of {@link #createInventory(InventoryHolder, InventoryType, net.kyori.adventure.text.Component)}
+     */
+    @Deprecated // Paper
+    @NotNull
     public static Inventory createInventory(@Nullable InventoryHolder owner, int size, @NotNull String title) throws IllegalArgumentException {
         return server.createInventory(owner, size, title);
     }
 
+    // Paper start
     /**
      * Creates an empty merchant.
      *
@@ -0,0 +0,0 @@ public final class Bukkit {
      * when the merchant inventory is viewed
      * @return a new merchant
      */
+    public static @NotNull Merchant createMerchant(net.kyori.adventure.text.@Nullable Component title) {
+        return server.createMerchant(title);
+    }
+    // Paper start
+    /**
+     * Creates an empty merchant.
+     *
+     * @param title the title of the corresponding merchant inventory, displayed
+     * when the merchant inventory is viewed
+     * @return a new merchant
+     * @deprecated in favour of {@link #createMerchant(net.kyori.adventure.text.Component)}
+     */
     @NotNull
+    @Deprecated // Paper
     public static Merchant createMerchant(@Nullable String title) {
         return server.createMerchant(title);
     }
@@ -0,0 +0,0 @@ public final class Bukkit {
         return server.isPrimaryThread();
     }
 
+    // Paper start
+    /**
+     * Gets the message that is displayed on the server list.
+     *
+     * @return the server's MOTD
+     */
+    @NotNull public static net.kyori.adventure.text.Component motd() {
+        return server.motd();
+    }
+
+    /**
+     * Set the message that is displayed on the server list.
+     *
+     * @param motd The message to be displayed
+     */
+    public static void motd(final net.kyori.adventure.text.@NotNull Component motd) {
+        server.motd(motd);
+    }
+
+    /**
+     * Gets the default message that is displayed when the server is stopped.
+     *
+     * @return the shutdown message
+     */
+    public static net.kyori.adventure.text.@Nullable Component shutdownMessage() {
+        return server.shutdownMessage();
+    }
+    // Paper end
+
     /**
      * Gets the message that is displayed on the server list.
      *
      * @return the servers MOTD
+     * @deprecated in favour of {@link #motd()}
      */
     @NotNull
+    @Deprecated // Paper
     public static String getMotd() {
         return server.getMotd();
     }
@@ -0,0 +0,0 @@ public final class Bukkit {
      * Set the message that is displayed on the server list.
      *
      * @param motd The message to be displayed
+     * @deprecated in favour of {@link #motd(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public static void setMotd(@NotNull String motd) {
         server.setMotd(motd);
     }
@@ -0,0 +0,0 @@ public final class Bukkit {
      * Gets the default message that is displayed when the server is stopped.
      *
      * @return the shutdown message
+     * @deprecated in favour of {@link #shutdownMessage()}
      */
     @Nullable
+    @Deprecated // Paper
     public static String getShutdownMessage() {
         return server.getShutdownMessage();
     }
diff --git a/src/main/java/org/bukkit/ChatColor.java b/src/main/java/org/bukkit/ChatColor.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/ChatColor.java
+++ b/src/main/java/org/bukkit/ChatColor.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
 
 /**
  * All supported color values for chat
+ * @deprecated ChatColor has been deprecated in favor of <a href="https://docs.advntr.dev/text.html">Adventure</a> API. See {@link net.kyori.adventure.text.format.NamedTextColor} for the adventure equivalent of pre-defined text colors
  */
+@Deprecated // Paper
 public enum ChatColor {
     /**
      * Represents black
diff --git a/src/main/java/org/bukkit/JukeboxSong.java b/src/main/java/org/bukkit/JukeboxSong.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/JukeboxSong.java
+++ b/src/main/java/org/bukkit/JukeboxSong.java
@@ -0,0 +0,0 @@ public interface JukeboxSong extends Keyed, Translatable {
     private static JukeboxSong get(@NotNull String key) {
         return Registry.JUKEBOX_SONG.getOrThrow(NamespacedKey.minecraft(key));
     }
+
+    // Paper start - adventure
+    /**
+     * @deprecated this method assumes that jukebox song description will
+     * always be a translatable component which is not guaranteed.
+     */
+    @Override
+    @Deprecated(forRemoval = true)
+    @org.jetbrains.annotations.NotNull String getTranslationKey();
+    // Paper end - adventure
 }
diff --git a/src/main/java/org/bukkit/Keyed.java b/src/main/java/org/bukkit/Keyed.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/Keyed.java
+++ b/src/main/java/org/bukkit/Keyed.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
 /**
  * Represents an object which has a {@link NamespacedKey} attached to it.
  */
-public interface Keyed {
+public interface Keyed extends net.kyori.adventure.key.Keyed { // Paper -- extend Adventure Keyed
 
     /**
      * Return the namespaced identifier for this object.
@@ -0,0 +0,0 @@ public interface Keyed {
      */
     @NotNull
     NamespacedKey getKey();
+
+    // Paper start
+    /**
+     * Returns the unique identifier for this object.
+     *
+     * @return this object's key
+     */
+    @Override
+    default net.kyori.adventure.key.@NotNull Key key() {
+        return this.getKey();
+    }
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/Nameable.java b/src/main/java/org/bukkit/Nameable.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/Nameable.java
+++ b/src/main/java/org/bukkit/Nameable.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  */
 public interface Nameable {
 
+    // Paper start
+    /**
+     * Gets the custom name.
+     *
+     * <p>This value has no effect on players, they will always use their real name.</p>
+     *
+     * @return the custom name
+     */
+    net.kyori.adventure.text.@Nullable Component customName();
+
+    /**
+     * Sets the custom name.
+     *
+     * <p>This name will be used in death messages and can be sent to the client as a nameplate over the mob.</p>
+     *
+     * <p>Setting the name to {@code null} will clear it.</p>
+     *
+     * <p>This value has no effect on players, they will always use their real name.</p>
+     *
+     * @param customName the custom name to set
+     */
+    void customName(final net.kyori.adventure.text.@Nullable Component customName);
+    // Paper end
+
     /**
      * Gets the custom name on a mob or block. If there is no name this method
      * will return null.
@@ -0,0 +0,0 @@ public interface Nameable {
      * This value has no effect on players, they will always use their real
      * name.
      *
+     * @deprecated in favour of {@link #customName()}
      * @return name of the mob/block or null
      */
+    @Deprecated // Paper
     @Nullable
     public String getCustomName();
 
@@ -0,0 +0,0 @@ public interface Nameable {
      * This value has no effect on players, they will always use their real
      * name.
      *
+     * @deprecated in favour of {@link #customName(net.kyori.adventure.text.Component)}
      * @param name the name to set
      */
+    @Deprecated // Paper
     public void setCustomName(@Nullable String name);
 }
diff --git a/src/main/java/org/bukkit/NamespacedKey.java b/src/main/java/org/bukkit/NamespacedKey.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/NamespacedKey.java
+++ b/src/main/java/org/bukkit/NamespacedKey.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  * underscores, hyphens, and forward slashes.
  *
  */
-public final class NamespacedKey {
+public final class NamespacedKey implements net.kyori.adventure.key.Key { // Paper - implement Key
 
     /**
      * The namespace representing all inbuilt keys.
@@ -0,0 +0,0 @@ public final class NamespacedKey {
 
     @Override
     public int hashCode() {
-        int hash = 5;
-        hash = 47 * hash + this.namespace.hashCode();
-        hash = 47 * hash + this.key.hashCode();
-        return hash;
+        // Paper start
+        int result = this.namespace.hashCode();
+        result = (31 * result) + this.key.hashCode();
+        return result;
+        // Paper end
     }
 
     @Override
@@ -0,0 +0,0 @@ public final class NamespacedKey {
         if (obj == null) {
             return false;
         }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final NamespacedKey other = (NamespacedKey) obj;
-        return this.namespace.equals(other.namespace) && this.key.equals(other.key);
+        // Paper start
+        if (!(obj instanceof net.kyori.adventure.key.Key key)) return false;
+        return this.namespace.equals(key.namespace()) && this.key.equals(key.value());
+        // Paper end
     }
 
     @Override
@@ -0,0 +0,0 @@ public final class NamespacedKey {
     public static NamespacedKey fromString(@NotNull String key) {
         return fromString(key, null);
     }
+
+    // Paper start
+    @NotNull
+    @Override
+    public String namespace() {
+        return this.getNamespace();
+    }
+
+    @NotNull
+    @Override
+    public String value() {
+        return this.getKey();
+    }
+
+    @NotNull
+    @Override
+    public String asString() {
+        return this.namespace + ':' + this.key;
+    }
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
 /**
  * Represents a server implementation.
  */
-public interface Server extends PluginMessageRecipient {
+public interface Server extends PluginMessageRecipient, net.kyori.adventure.audience.ForwardingAudience { // Paper
 
     /**
      * Used for all administrative messages, such as an operator using a
      * command.
      * <p>
-     * For use in {@link #broadcast(java.lang.String, java.lang.String)}.
+     * For use in {@link #broadcast(net.kyori.adventure.text.Component, java.lang.String)}.
      */
     public static final String BROADCAST_CHANNEL_ADMINISTRATIVE = "bukkit.broadcast.admin";
 
@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
      * Used for all announcement messages, such as informing users that a
      * player has joined.
      * <p>
-     * For use in {@link #broadcast(java.lang.String, java.lang.String)}.
+     * For use in {@link #broadcast(net.kyori.adventure.text.Component, java.lang.String)}.
      */
     public static final String BROADCAST_CHANNEL_USERS = "bukkit.broadcast.user";
 
@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
      *
      * @param message the message
      * @return the number of players
+     * @deprecated use {@link #broadcast(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public int broadcastMessage(@NotNull String message);
 
     /**
@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
      * @param permission the required permission {@link Permissible
      *     permissibles} must have to receive the broadcast
      * @return number of message recipients
+     * @deprecated in favour of {@link #broadcast(net.kyori.adventure.text.Component, String)}
      */
+    @Deprecated // Paper
     public int broadcast(@NotNull String message, @NotNull String permission);
+    // Paper start
+    /**
+     * Broadcast a message to all players.
+     * <p>
+     * This is the same as calling {@link #broadcast(net.kyori.adventure.text.Component,
+     * java.lang.String)} with the {@link #BROADCAST_CHANNEL_USERS} permission.
+     *
+     * @param message the message
+     * @return the number of players
+     */
+    int broadcast(net.kyori.adventure.text.@NotNull Component message);
+
+    /**
+     * Broadcasts the specified message to every user with the given
+     * permission name.
+     *
+     * @param message message to broadcast
+     * @param permission the required permission {@link Permissible
+     *     permissibles} must have to receive the broadcast
+     * @return number of message recipients
+     */
+    int broadcast(net.kyori.adventure.text.@NotNull Component message, @NotNull String permission);
+    // Paper end
 
     /**
      * Gets the player by the given name, regardless if they are offline or
@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
     @NotNull
     Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type);
 
+    // Paper start
+    /**
+     * Creates an empty inventory with the specified type and title. If the type
+     * is {@link InventoryType#CHEST}, the new inventory has a size of 27;
+     * otherwise the new inventory has the normal size for its type.<br>
+     * It should be noted that some inventory types do not support titles and
+     * may not render with said titles on the Minecraft client.
+     * <br>
+     * {@link InventoryType#WORKBENCH} will not process crafting recipes if
+     * created with this method. Use
+     * {@link Player#openWorkbench(Location, boolean)} instead.
+     * <br>
+     * {@link InventoryType#ENCHANTING} will not process {@link ItemStack}s
+     * for possible enchanting results. Use
+     * {@link Player#openEnchanting(Location, boolean)} instead.
+     *
+     * @param owner The holder of the inventory; can be null if there's no holder.
+     * @param type The type of inventory to create.
+     * @param title The title of the inventory, to be displayed when it is viewed.
+     * @return The new inventory.
+     * @throws IllegalArgumentException if the {@link InventoryType} cannot be
+     * viewed.
+     *
+     * @see InventoryType#isCreatable()
+     */
+    @NotNull
+    Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type, net.kyori.adventure.text.@NotNull Component title);
+    // Paper end
+
     /**
      * Creates an empty inventory with the specified type and title. If the type
      * is {@link InventoryType#CHEST}, the new inventory has a size of 27;
@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
      * @return The new inventory.
      * @throws IllegalArgumentException if the {@link InventoryType} cannot be
      * viewed.
+     * @deprecated in favour of {@link #createInventory(InventoryHolder, InventoryType, net.kyori.adventure.text.Component)}
      *
      * @see InventoryType#isCreatable()
      */
+    @Deprecated // Paper
     @NotNull
     Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type, @NotNull String title);
 
@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
     @NotNull
     Inventory createInventory(@Nullable InventoryHolder owner, int size) throws IllegalArgumentException;
 
+    // Paper start
+    /**
+     * Creates an empty inventory of type {@link InventoryType#CHEST} with the
+     * specified size and title.
+     *
+     * @param owner the holder of the inventory, or null to indicate no holder
+     * @param size a multiple of 9 as the size of inventory to create
+     * @param title the title of the inventory, displayed when inventory is
+     *     viewed
+     * @return a new inventory
+     * @throws IllegalArgumentException if the size is not a multiple of 9
+     */
+    @NotNull
+    Inventory createInventory(@Nullable InventoryHolder owner, int size, net.kyori.adventure.text.@NotNull Component title) throws IllegalArgumentException;
+    // Paper end
+
     /**
      * Creates an empty inventory of type {@link InventoryType#CHEST} with the
      * specified size and title.
@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
      *     viewed
      * @return a new inventory
      * @throws IllegalArgumentException if the size is not a multiple of 9
+     * @deprecated in favour of {@link #createInventory(InventoryHolder, int, net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     @NotNull
     Inventory createInventory(@Nullable InventoryHolder owner, int size, @NotNull String title) throws IllegalArgumentException;
 
+    // Paper start
+    /**
+     * Creates an empty merchant.
+     *
+     * @param title the title of the corresponding merchant inventory, displayed
+     * when the merchant inventory is viewed
+     * @return a new merchant
+     */
+    @NotNull Merchant createMerchant(net.kyori.adventure.text.@Nullable Component title);
+    // Paper start
     /**
      * Creates an empty merchant.
      *
      * @param title the title of the corresponding merchant inventory, displayed
      * when the merchant inventory is viewed
      * @return a new merchant
+     * @deprecated in favour of {@link #createMerchant(net.kyori.adventure.text.Component)}
      */
     @NotNull
+    @Deprecated // Paper
     Merchant createMerchant(@Nullable String title);
 
     /**
@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
      */
     boolean isPrimaryThread();
 
+    // Paper start
+    /**
+     * Gets the message that is displayed on the server list.
+     *
+     * @return the server's MOTD
+     */
+    net.kyori.adventure.text.@NotNull Component motd();
+
+    /**
+     * Set the message that is displayed on the server list.
+     *
+     * @param motd The message to be displayed
+     */
+    void motd(final net.kyori.adventure.text.@NotNull Component motd);
+
+    /**
+     * Gets the default message that is displayed when the server is stopped.
+     *
+     * @return the shutdown message
+     */
+    net.kyori.adventure.text.@Nullable Component shutdownMessage();
+    // Paper end
+
     /**
      * Gets the message that is displayed on the server list.
      *
      * @return the servers MOTD
+     * @deprecated in favour of {@link #motd()}
      */
     @NotNull
+    @Deprecated // Paper
     String getMotd();
 
     /**
      * Set the message that is displayed on the server list.
      *
      * @param motd The message to be displayed
+     * @deprecated in favour of {@link #motd(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     void setMotd(@NotNull String motd);
 
     /**
@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
      * Gets the default message that is displayed when the server is stopped.
      *
      * @return the shutdown message
+     * @deprecated in favour of {@link #shutdownMessage()}
      */
     @Nullable
+    @Deprecated // Paper
     String getShutdownMessage();
 
     /**
@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
          * Sends the component to the player
          *
          * @param component the components to send
+         * @deprecated use {@link #broadcast(net.kyori.adventure.text.Component)}
          */
+        @Deprecated // Paper
         public void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
          * Sends an array of components as a single message to the player
          *
          * @param components the components to send
+         * @deprecated use {@link #broadcast(net.kyori.adventure.text.Component)}
          */
+        @Deprecated // Paper
         public void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
diff --git a/src/main/java/org/bukkit/ServerLinks.java b/src/main/java/org/bukkit/ServerLinks.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/ServerLinks.java
+++ b/src/main/java/org/bukkit/ServerLinks.java
@@ -0,0 +0,0 @@ public interface ServerLinks {
     @NotNull
     ServerLink addLink(@NotNull Type type, @NotNull URI url);
 
+    // Paper start - Adventure
+    /**
+     * Adds the given link to the list of links.
+     *
+     * @param displayName link name / display text Component
+     * @param url link url
+     * @return the added link
+     */
+    @NotNull
+    ServerLink addLink(@NotNull net.kyori.adventure.text.Component displayName, @NotNull URI url);
+    // Paper end - Adventure
+
     /**
      * Adds the given link to the list of links.
      *
      * @param displayName link name / display text
      * @param url link url
      * @return the added link
+     * @deprecated in favour of {@link ServerLinks#addLink(net.kyori.adventure.text.Component, URI)}
      */
+    @Deprecated // Paper - Adventure
     @NotNull
     ServerLink addLink(@NotNull String displayName, @NotNull URI url);
 
@@ -0,0 +0,0 @@ public interface ServerLinks {
         @Nullable
         Type getType();
 
+        // Paper start - Adventure
+        /**
+         * Gets the display name/text Component of this link.
+         *
+         * @return display name Component
+         */
+        @NotNull
+        net.kyori.adventure.text.Component displayName();
+        // Paper end - Adventure
+
         /**
          * Gets the display name/text of this link.
          *
          * @return display name
+         * @deprecated in favour of {@link ServerLink#displayName()}
          */
+        @Deprecated // Paper - Adventure
         @NotNull
         String getDisplayName();
 
diff --git a/src/main/java/org/bukkit/Sound.java b/src/main/java/org/bukkit/Sound.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/Sound.java
+++ b/src/main/java/org/bukkit/Sound.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
  * guarantee values will not be removed from this interface. As such, you should not
  * depend on the ordinal values of this class.
  */
-public interface Sound extends OldEnum<Sound>, Keyed {
+public interface Sound extends OldEnum<Sound>, Keyed, net.kyori.adventure.sound.Sound.Type { // Paper - implement Sound.Type
 
     Sound AMBIENT_BASALT_DELTAS_ADDITIONS = getSound("ambient.basalt_deltas.additions");
     Sound AMBIENT_BASALT_DELTAS_LOOP = getSound("ambient.basalt_deltas.loop");
@@ -0,0 +0,0 @@ public interface Sound extends OldEnum<Sound>, Keyed {
     static Sound[] values() {
         return Lists.newArrayList(Registry.SOUNDS).toArray(new Sound[0]);
     }
+
+    // Paper start
+    @Override
+    default net.kyori.adventure.key.@NotNull Key key() {
+        return this.getKey();
+    }
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/SoundCategory.java b/src/main/java/org/bukkit/SoundCategory.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/SoundCategory.java
+++ b/src/main/java/org/bukkit/SoundCategory.java
@@ -0,0 +0,0 @@ package org.bukkit;
 /**
  * An Enum of categories for sounds.
  */
-public enum SoundCategory {
+public enum SoundCategory implements net.kyori.adventure.sound.Sound.Source.Provider { // Paper - implement Sound.Source.Provider
 
     MASTER,
     MUSIC,
@@ -0,0 +0,0 @@ public enum SoundCategory {
     PLAYERS,
     AMBIENT,
     VOICE;
+
+    // Paper start - implement Sound.Source.Provider
+    @Override
+    public net.kyori.adventure.sound.Sound.@org.jetbrains.annotations.NotNull Source soundSource() {
+        return switch (this) {
+            case MASTER -> net.kyori.adventure.sound.Sound.Source.MASTER;
+            case MUSIC -> net.kyori.adventure.sound.Sound.Source.MUSIC;
+            case RECORDS -> net.kyori.adventure.sound.Sound.Source.RECORD;
+            case WEATHER -> net.kyori.adventure.sound.Sound.Source.WEATHER;
+            case BLOCKS -> net.kyori.adventure.sound.Sound.Source.BLOCK;
+            case HOSTILE -> net.kyori.adventure.sound.Sound.Source.HOSTILE;
+            case NEUTRAL -> net.kyori.adventure.sound.Sound.Source.NEUTRAL;
+            case PLAYERS -> net.kyori.adventure.sound.Sound.Source.PLAYER;
+            case AMBIENT -> net.kyori.adventure.sound.Sound.Source.AMBIENT;
+            case VOICE -> net.kyori.adventure.sound.Sound.Source.VOICE;
+        };
+    }
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/UnsafeValues.java
+++ b/src/main/java/org/bukkit/UnsafeValues.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  */
 @Deprecated
 public interface UnsafeValues {
+    // Paper start
+    net.kyori.adventure.text.flattener.ComponentFlattener componentFlattener();
+    @Deprecated(forRemoval = true) net.kyori.adventure.text.serializer.plain.PlainComponentSerializer plainComponentSerializer();
+    @Deprecated(forRemoval = true) net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer plainTextSerializer();
+    @Deprecated(forRemoval = true) net.kyori.adventure.text.serializer.gson.GsonComponentSerializer gsonComponentSerializer();
+    @Deprecated(forRemoval = true) net.kyori.adventure.text.serializer.gson.GsonComponentSerializer colorDownsamplingGsonComponentSerializer();
+    @Deprecated(forRemoval = true) net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer legacyComponentSerializer();
+    net.kyori.adventure.text.Component resolveWithContext(net.kyori.adventure.text.Component component, org.bukkit.command.CommandSender context, org.bukkit.entity.Entity scoreboardSubject, boolean bypassPermissions) throws java.io.IOException;
+    // Paper end
 
     Material toLegacy(Material material);
 
diff --git a/src/main/java/org/bukkit/Warning.java b/src/main/java/org/bukkit/Warning.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/Warning.java
+++ b/src/main/java/org/bukkit/Warning.java
@@ -0,0 +0,0 @@ public @interface Warning {
          *     </ul>
          */
         public boolean printFor(@Nullable Warning warning) {
+            if (Boolean.getBoolean("paper.alwaysPrintWarningState")) return true; // Paper
             if (this == DEFAULT) {
                 return warning == null || warning.value();
             }
diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/World.java
+++ b/src/main/java/org/bukkit/World.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
 /**
  * Represents a world, which may contain entities, chunks and blocks
  */
-public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient, Metadatable, PersistentDataHolder, Keyed {
+public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient, Metadatable, PersistentDataHolder, Keyed, net.kyori.adventure.audience.ForwardingAudience { // Paper
 
     /**
      * Gets the {@link Block} at the given coordinates
@@ -0,0 +0,0 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
     @NotNull
     public List<Player> getPlayers();
 
+    // Paper start
+    @NotNull
+    @Override
+    default Iterable<? extends net.kyori.adventure.audience.Audience> audiences() {
+        return this.getPlayers();
+    }
+    // Paper end
+
     /**
      * Returns a list of entities within a bounding box centered around a
      * Location.
diff --git a/src/main/java/org/bukkit/block/CommandBlock.java b/src/main/java/org/bukkit/block/CommandBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/block/CommandBlock.java
+++ b/src/main/java/org/bukkit/block/CommandBlock.java
@@ -0,0 +0,0 @@ public interface CommandBlock extends TileState {
      * by default is "@".
      *
      * @return Name of this CommandBlock.
+     * @deprecated in favour of {@link #name()}
      */
+    @Deprecated // Paper
     @NotNull
     public String getName();
 
@@ -0,0 +0,0 @@ public interface CommandBlock extends TileState {
      * same as setting it to "@".
      *
      * @param name New name for this CommandBlock.
+     * @deprecated in favour of {@link #name(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setName(@Nullable String name);
+
+    // Paper start
+    /**
+     * Gets the name of this CommandBlock.  The name is used with commands
+     * that this CommandBlock executes.  This name will never be null, and
+     * by default is a {@link net.kyori.adventure.text.TextComponent} containing {@code @}.
+     *
+     * @return Name of this CommandBlock.
+     */
+    public net.kyori.adventure.text.@NotNull Component name();
+
+    /**
+     * Sets the name of this CommandBlock.  The name is used with commands
+     * that this CommandBlock executes.  Setting the name to null is the
+     * same as setting it to a {@link net.kyori.adventure.text.TextComponent} containing {@code @}.
+     *
+     * @param name New name for this CommandBlock.
+     */
+    public void name(net.kyori.adventure.text.@Nullable Component name);
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/block/Sign.java b/src/main/java/org/bukkit/block/Sign.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/block/Sign.java
+++ b/src/main/java/org/bukkit/block/Sign.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  * Represents a captured state of either a SignPost or a WallSign.
  */
 public interface Sign extends TileState, Colorable {
+    // Paper start
+    /**
+     * Gets all the lines of text currently on the {@link Side#FRONT} of this sign.
+     *
+     * @return List of components containing each line of text
+     * @deprecated A sign may have multiple writable sides now. Use {@link Sign#getSide(Side)} and {@link SignSide#lines()}.
+     */
+    @NotNull
+    @Deprecated
+    public java.util.List<net.kyori.adventure.text.Component> lines();
+
+    /**
+     * Gets the line of text at the specified index on the {@link Side#FRONT}.
+     * <p>
+     * For example, getLine(0) will return the first line of text.
+     *
+     * @param index Line number to get the text from, starting at 0
+     * @throws IndexOutOfBoundsException Thrown when the line does not exist
+     * @return Text on the given line
+     * @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int)}.
+     */
+    @NotNull
+    @Deprecated
+    public net.kyori.adventure.text.Component line(int index) throws IndexOutOfBoundsException;
+
+    /**
+     * Sets the line of text at the specified index on the {@link Side#FRONT}.
+     * <p>
+     * For example, setLine(0, "Line One") will set the first line of text to
+     * "Line One".
+     *
+     * @param index Line number to set the text at, starting from 0
+     * @param line New text to set at the specified index
+     * @throws IndexOutOfBoundsException If the index is out of the range 0..3
+     * @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int, net.kyori.adventure.text.Component)}.
+     */
+    @Deprecated
+    public void line(int index, net.kyori.adventure.text.@NotNull Component line) throws IndexOutOfBoundsException;
+    // Paper end
 
     /**
      * Gets all the lines of text currently on the {@link Side#FRONT} of this sign.
      *
      * @return Array of Strings containing each line of text
-     * @deprecated  A sign may have multiple writable sides now. Use {@link Sign#getSide(Side)} and {@link SignSide#getLines()}.
+     * @deprecated  A sign may have multiple writable sides now. Use {@link Sign#getSide(Side)} and {@link SignSide#lines()}.
      */
     @Deprecated
     @NotNull
@@ -0,0 +0,0 @@ public interface Sign extends TileState, Colorable {
      * @param index Line number to get the text from, starting at 0
      * @return Text on the given line
      * @throws IndexOutOfBoundsException Thrown when the line does not exist
-     * @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#getLine(int)}.
+     * @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int)}.
      */
     @Deprecated
     @NotNull
@@ -0,0 +0,0 @@ public interface Sign extends TileState, Colorable {
      * @param index Line number to set the text at, starting from 0
      * @param line New text to set at the specified index
      * @throws IndexOutOfBoundsException If the index is out of the range 0..3
-     * @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#setLine(int, String)}.
+     * @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int, net.kyori.adventure.text.Component)}.
      */
     @Deprecated
     public void setLine(int index, @NotNull String line) throws IndexOutOfBoundsException;
diff --git a/src/main/java/org/bukkit/block/sign/SignSide.java b/src/main/java/org/bukkit/block/sign/SignSide.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/block/sign/SignSide.java
+++ b/src/main/java/org/bukkit/block/sign/SignSide.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
  * Represents a side of a sign.
  */
 public interface SignSide extends Colorable {
+    // Paper start
+    /**
+     * Gets all the lines of text currently on the sign.
+     *
+     * @return List of components containing each line of text
+     */
+    @NotNull
+    public java.util.List<net.kyori.adventure.text.Component> lines();
+
+    /**
+     * Gets the line of text at the specified index.
+     * <p>
+     * For example, getLine(0) will return the first line of text.
+     *
+     * @param index Line number to get the text from, starting at 0
+     * @throws IndexOutOfBoundsException Thrown when the line does not exist
+     * @return Text on the given line
+     */
+    @NotNull
+    public net.kyori.adventure.text.Component line(int index) throws IndexOutOfBoundsException;
+
+    /**
+     * Sets the line of text at the specified index.
+     * <p>
+     * For example, setLine(0, "Line One") will set the first line of text to
+     * "Line One".
+     *
+     * @param index Line number to set the text at, starting from 0
+     * @param line New text to set at the specified index
+     * @throws IndexOutOfBoundsException If the index is out of the range 0..3
+     */
+    public void line(int index, net.kyori.adventure.text.@NotNull Component line) throws IndexOutOfBoundsException;
+    // Paper end
 
     /**
      * Gets all the lines of text currently on this side of the sign.
      *
      * @return Array of Strings containing each line of text
+     * @deprecated in favour of {@link #lines()}
      */
     @NotNull
+    @Deprecated // Paper
     public String[] getLines();
 
     /**
@@ -0,0 +0,0 @@ public interface SignSide extends Colorable {
      * @param index Line number to get the text from, starting at 0
      * @return Text on the given line
      * @throws IndexOutOfBoundsException Thrown when the line does not exist
+     * @deprecated in favour of {@link #line(int)}
      */
     @NotNull
+    @Deprecated // Paper
     public String getLine(int index) throws IndexOutOfBoundsException;
 
     /**
@@ -0,0 +0,0 @@ public interface SignSide extends Colorable {
      * @param index Line number to set the text at, starting from 0
      * @param line New text to set at the specified index
      * @throws IndexOutOfBoundsException If the index is out of the range 0..3
+     * @deprecated in favour of {@link #line(int, net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setLine(int index, @NotNull String line) throws IndexOutOfBoundsException;
 
     /**
diff --git a/src/main/java/org/bukkit/command/Command.java b/src/main/java/org/bukkit/command/Command.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/command/Command.java
+++ b/src/main/java/org/bukkit/command/Command.java
@@ -0,0 +0,0 @@ public abstract class Command {
     protected String description;
     protected String usageMessage;
     private String permission;
-    private String permissionMessage;
+    private net.kyori.adventure.text.Component permissionMessage; // Paper
     public org.spigotmc.CustomTimingsHandler timings; // Spigot
 
     protected Command(@NotNull String name) {
@@ -0,0 +0,0 @@ public abstract class Command {
 
         if (permissionMessage == null) {
             target.sendMessage(ChatColor.RED + "I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is a mistake.");
-        } else if (permissionMessage.length() != 0) {
-            for (String line : permissionMessage.replace("<permission>", permission).split("\n")) {
-                target.sendMessage(line);
-            }
+            // Paper start - use components for permissionMessage
+        } else if (!permissionMessage.equals(net.kyori.adventure.text.Component.empty())) {
+            target.sendMessage(permissionMessage.replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("<permission>").replacement(permission).build()));
+            // Paper end
         }
 
         return false;
@@ -0,0 +0,0 @@ public abstract class Command {
     @Deprecated
     @Nullable
     public String getPermissionMessage() {
-        return permissionMessage;
+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serializeOrNull(permissionMessage); // Paper
     }
 
     /**
@@ -0,0 +0,0 @@ public abstract class Command {
     @Deprecated
     @NotNull
     public Command setPermissionMessage(@Nullable String permissionMessage) {
-        this.permissionMessage = permissionMessage;
+        this.permissionMessage = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserializeOrNull(permissionMessage); // Paper
         return this;
     }
 
@@ -0,0 +0,0 @@ public abstract class Command {
         this.usageMessage = (usage == null) ? "" : usage;
         return this;
     }
+    // Paper start
+    /**
+     * Gets the permission message.
+     *
+     * @return the permission message
+     * @deprecated permission messages have not worked for player-executed
+     * commands since 1.13 as clients without permission to execute a command
+     * are unaware of its existence and therefore will not send an unknown
+     * command execution to the server. This message will only ever be shown to
+     * consoles or when this command is executed with
+     * {@link Bukkit#dispatchCommand(CommandSender, String)}.
+     */
+    @Deprecated
+    public net.kyori.adventure.text.@Nullable Component permissionMessage() {
+        return this.permissionMessage;
+    }
+
+    /**
+     * Sets the permission message.
+     *
+     * @param permissionMessage the permission message
+     * @deprecated permission messages have not worked for player-executed
+     * commands since 1.13 as clients without permission to execute a command
+     * are unaware of its existence and therefore will not send an unknown
+     * command execution to the server. This message will only ever be shown to
+     * consoles or when this command is executed with
+     * {@link Bukkit#dispatchCommand(CommandSender, String)}.
+     */
+    @Deprecated
+    public void permissionMessage(net.kyori.adventure.text.@Nullable Component permissionMessage) {
+        this.permissionMessage = permissionMessage;
+    }
+    // Paper end
 
     public static void broadcastCommandMessage(@NotNull CommandSender source, @NotNull String message) {
         broadcastCommandMessage(source, message, true);
     }
 
     public static void broadcastCommandMessage(@NotNull CommandSender source, @NotNull String message, boolean sendToSource) {
-        String result = source.getName() + ": " + message;
+        // Paper start
+        broadcastCommandMessage(source, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message), sendToSource);
+    }
+
+    public static void broadcastCommandMessage(@NotNull CommandSender source, net.kyori.adventure.text.@NotNull Component message) {
+        broadcastCommandMessage(source, message, true);
+    }
+
+    public static void broadcastCommandMessage(@NotNull CommandSender source, net.kyori.adventure.text.@NotNull Component message, boolean sendToSource) {
+        net.kyori.adventure.text.TextComponent.Builder result = net.kyori.adventure.text.Component.text()
+            .color(net.kyori.adventure.text.format.NamedTextColor.WHITE)
+            .decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false)
+            .append(source.name())
+            .append(net.kyori.adventure.text.Component.text(": "))
+            .append(message);
+        // Paper end
 
         if (source instanceof BlockCommandSender) {
             BlockCommandSender blockCommandSender = (BlockCommandSender) source;
@@ -0,0 +0,0 @@ public abstract class Command {
         }
 
         Set<Permissible> users = Bukkit.getPluginManager().getPermissionSubscriptions(Server.BROADCAST_CHANNEL_ADMINISTRATIVE);
-        String colored = ChatColor.GRAY + "" + ChatColor.ITALIC + "[" + result + ChatColor.GRAY + ChatColor.ITALIC + "]";
+        // Paper start
+        net.kyori.adventure.text.TextComponent.Builder colored = net.kyori.adventure.text.Component.text()
+            .color(net.kyori.adventure.text.format.NamedTextColor.GRAY)
+            .decorate(net.kyori.adventure.text.format.TextDecoration.ITALIC)
+            .append(net.kyori.adventure.text.Component.text("["), result, net.kyori.adventure.text.Component.text("]"));
+        // Paper end
 
         if (sendToSource && !(source instanceof ConsoleCommandSender)) {
             source.sendMessage(message);
diff --git a/src/main/java/org/bukkit/command/CommandSender.java b/src/main/java/org/bukkit/command/CommandSender.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/command/CommandSender.java
+++ b/src/main/java/org/bukkit/command/CommandSender.java
@@ -0,0 +0,0 @@ import org.bukkit.permissions.Permissible;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-public interface CommandSender extends Permissible {
+public interface CommandSender extends net.kyori.adventure.audience.Audience, Permissible { // Paper
 
     /**
      * Sends this sender a message
      *
      * @param message Message to be displayed
+     * @see #sendMessage(net.kyori.adventure.text.Component)
+     * @see #sendPlainMessage(String)
+     * @see #sendRichMessage(String)
      */
+    @org.jetbrains.annotations.ApiStatus.Obsolete // Paper
     public void sendMessage(@NotNull String message);
 
     /**
      * Sends this sender multiple messages
      *
      * @param messages An array of messages to be displayed
+     * @see #sendMessage(net.kyori.adventure.text.Component)
+     * @see #sendPlainMessage(String)
+     * @see #sendRichMessage(String)
      */
+    @org.jetbrains.annotations.ApiStatus.Obsolete // Paper
     public void sendMessage(@NotNull String... messages);
 
     /**
@@ -0,0 +0,0 @@ public interface CommandSender extends Permissible {
      *
      * @param message Message to be displayed
      * @param sender The sender of this message
+     * @see #sendMessage(net.kyori.adventure.identity.Identified, net.kyori.adventure.text.Component)
+     * @deprecated sender UUID is ignored
      */
+    @Deprecated // Paper
     public void sendMessage(@Nullable UUID sender, @NotNull String message);
 
     /**
@@ -0,0 +0,0 @@ public interface CommandSender extends Permissible {
      *
      * @param messages An array of messages to be displayed
      * @param sender The sender of this message
+     * @see #sendMessage(net.kyori.adventure.identity.Identified, net.kyori.adventure.text.Component)
+     * @deprecated sender UUID is ignored
      */
+    @Deprecated // Paper
     public void sendMessage(@Nullable UUID sender, @NotNull String... messages);
 
     /**
@@ -0,0 +0,0 @@ public interface CommandSender extends Permissible {
          * Sends this sender a chat component.
          *
          * @param component the components to send
+         * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
          */
+        @Deprecated // Paper
         public void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface CommandSender extends Permissible {
          * Sends an array of components as a single message to the sender.
          *
          * @param components the components to send
+         * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
          */
+        @Deprecated // Paper
         public void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface CommandSender extends Permissible {
          *
          * @param component the components to send
          * @param sender the sender of the message
+         * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
          */
+        @Deprecated // Paper
         public void sendMessage(@Nullable UUID sender, @NotNull net.md_5.bungee.api.chat.BaseComponent component) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface CommandSender extends Permissible {
          *
          * @param components the components to send
          * @param sender the sender of the message
+         * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
          */
+        @Deprecated // Paper
         public void sendMessage(@Nullable UUID sender, @NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface CommandSender extends Permissible {
     @NotNull
     Spigot spigot();
     // Spigot end
+
+    // Paper start
+    /**
+     * Gets the name of this command sender
+     *
+     * @return Name of the sender
+     */
+    public net.kyori.adventure.text.@NotNull Component name();
+
+    @Override
+    default void sendMessage(final net.kyori.adventure.identity.@NotNull Identity identity, final net.kyori.adventure.text.@NotNull Component message, final net.kyori.adventure.audience.@NotNull MessageType type) {
+        this.sendMessage(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(message));
+    }
+
+    /**
+     * Sends a message with the MiniMessage format to the command sender.
+     * <p>
+     * See <a href="https://docs.advntr.dev/minimessage/">MiniMessage docs</a>
+     * for more information on the format.
+     *
+     * @param message MiniMessage content
+     */
+    default void sendRichMessage(final @NotNull String message) {
+        this.sendMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message));
+    }
+
+    /**
+     * Sends a message with the MiniMessage format to the command sender.
+     * <p>
+     * See <a href="https://docs.advntr.dev/minimessage/">MiniMessage docs</a> and <a href="https://docs.advntr.dev/minimessage/dynamic-replacements">MiniMessage Placeholders docs</a>
+     * for more information on the format.
+     *
+     * @param message MiniMessage content
+     * @param resolvers resolvers to use
+     */
+    default void sendRichMessage(final @NotNull String message, final net.kyori.adventure.text.minimessage.tag.resolver.@NotNull TagResolver... resolvers) {
+        this.sendMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message, resolvers));
+    }
+
+    /**
+     * Sends a plain message to the command sender.
+     *
+     * @param message plain message
+     */
+    default void sendPlainMessage(final @NotNull String message) {
+        this.sendMessage(net.kyori.adventure.text.Component.text(message));
+    }
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/command/PluginCommandYamlParser.java b/src/main/java/org/bukkit/command/PluginCommandYamlParser.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/command/PluginCommandYamlParser.java
+++ b/src/main/java/org/bukkit/command/PluginCommandYamlParser.java
@@ -0,0 +0,0 @@ public class PluginCommandYamlParser {
             }
 
             if (permissionMessage != null) {
-                newCmd.setPermissionMessage(permissionMessage.toString());
+                newCmd.permissionMessage(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(permissionMessage.toString())); // Paper
             }
 
             pluginCmds.add(newCmd);
diff --git a/src/main/java/org/bukkit/command/ProxiedCommandSender.java b/src/main/java/org/bukkit/command/ProxiedCommandSender.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/command/ProxiedCommandSender.java
+++ b/src/main/java/org/bukkit/command/ProxiedCommandSender.java
@@ -0,0 +0,0 @@ package org.bukkit.command;
 
 import org.jetbrains.annotations.NotNull;
 
-public interface ProxiedCommandSender extends CommandSender {
+public interface ProxiedCommandSender extends CommandSender, net.kyori.adventure.audience.ForwardingAudience.Single { // Paper
 
     /**
      * Returns the CommandSender which triggered this proxied command
@@ -0,0 +0,0 @@ public interface ProxiedCommandSender extends CommandSender {
     @NotNull
     CommandSender getCallee();
 
+    // Paper start
+    @Override
+    default void sendMessage(final net.kyori.adventure.identity.@NotNull Identity source, final net.kyori.adventure.text.@NotNull Component message, final net.kyori.adventure.audience.@NotNull MessageType type) {
+        net.kyori.adventure.audience.ForwardingAudience.Single.super.sendMessage(source, message, type);
+    }
+
+    @NotNull
+    @Override
+    default net.kyori.adventure.audience.Audience audience() {
+        return this.getCaller();
+    }
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/configuration/ConfigurationSection.java b/src/main/java/org/bukkit/configuration/ConfigurationSection.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/configuration/ConfigurationSection.java
+++ b/src/main/java/org/bukkit/configuration/ConfigurationSection.java
@@ -0,0 +0,0 @@ public interface ConfigurationSection {
      * one line.
      */
     public void setInlineComments(@NotNull String path, @Nullable List<String> comments);
+
+    // Paper start - add rich message component support to configuration
+    /**
+     * Gets the requested MiniMessage formatted String as Component by path.
+     * <p>
+     * If the Component does not exist but a default value has been specified,
+     * this will return the default value. If the Component does not exist and no
+     * default value was specified, this will return null.
+     *
+     * @param path Path of the Component to get.
+     * @return Requested Component.
+     */
+    default net.kyori.adventure.text.@Nullable Component getRichMessage(final @NotNull String path) {
+        return this.getRichMessage(path, null);
+    }
+
+    /**
+     * Gets the requested MiniMessage formatted String as Component by path.
+     * <p>
+     * If the Component does not exist but a default value has been specified,
+     * this will return the default value. If the Component does not exist and no
+     * default value was specified, this will return null.
+     *
+     * @param path Path of the Component to get.
+     * @param fallback component that will be used as fallback
+     * @return Requested Component.
+     */
+    @Contract("_, !null -> !null")
+    default net.kyori.adventure.text.@Nullable Component getRichMessage(final @NotNull String path, final net.kyori.adventure.text.@Nullable Component fallback) {
+        return this.getComponent(path, net.kyori.adventure.text.minimessage.MiniMessage.miniMessage(), fallback);
+    }
+
+    /**
+     * Sets the specified path to the given value.
+     * <p>
+     * If value is null, the entry will be removed. Any existing entry will be
+     * replaced, regardless of what the new value is.
+     *
+     * @param path Path of the object to set.
+     * @param value New value to set the path to.
+     */
+    default void setRichMessage(final @NotNull String path, final net.kyori.adventure.text.@Nullable Component value) {
+        this.setComponent(path, net.kyori.adventure.text.minimessage.MiniMessage.miniMessage(), value);
+    }
+
+    /**
+     * Gets the requested formatted String as Component by path deserialized by the ComponentDecoder.
+     * <p>
+     * If the Component does not exist but a default value has been specified,
+     * this will return the default value. If the Component does not exist and no
+     * default value was specified, this will return null.
+     *
+     * @param path Path of the Component to get.
+     * @param decoder ComponentDecoder instance used for deserialization
+     * @return Requested Component.
+     */
+    default <C extends net.kyori.adventure.text.Component> @Nullable C getComponent(final @NotNull String path, final net.kyori.adventure.text.serializer.@NotNull ComponentDecoder<? super String, C> decoder) {
+        return this.getComponent(path, decoder, null);
+    }
+
+    /**
+     * Gets the requested formatted String as Component by path deserialized by the ComponentDecoder.
+     * <p>
+     * If the Component does not exist but a default value has been specified,
+     * this will return the default value. If the Component does not exist and no
+     * default value was specified, this will return null.
+     *
+     * @param path Path of the Component to get.
+     * @param decoder ComponentDecoder instance used for deserialization
+     * @param fallback component that will be used as fallback
+     * @return Requested Component.
+     */
+    @Contract("_, _, !null -> !null")
+    default <C extends net.kyori.adventure.text.Component> @Nullable C getComponent(final @NotNull String path, final net.kyori.adventure.text.serializer.@NotNull ComponentDecoder<? super String, C> decoder, final @Nullable C fallback) {
+        java.util.Objects.requireNonNull(decoder, "decoder");
+        final String value = this.getString(path);
+        return decoder.deserializeOr(value, fallback);
+    }
+
+    /**
+     * Sets the specified path to the given value.
+     * <p>
+     * If value is null, the entry will be removed. Any existing entry will be
+     * replaced, regardless of what the new value is.
+     *
+     * @param path Path of the object to set.
+     * @param encoder the encoder used to transform the value
+     * @param value New value to set the path to.
+     */
+    default <C extends net.kyori.adventure.text.Component> void setComponent(final @NotNull String path, final net.kyori.adventure.text.serializer.@NotNull ComponentEncoder<C, String> encoder, final @Nullable C value) {
+        java.util.Objects.requireNonNull(encoder, "encoder");
+        this.set(path, encoder.serializeOrNull(value));
+    }
+    // Paper end - add rich message component support to configuration
 }
diff --git a/src/main/java/org/bukkit/conversations/Conversable.java b/src/main/java/org/bukkit/conversations/Conversable.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/conversations/Conversable.java
+++ b/src/main/java/org/bukkit/conversations/Conversable.java
@@ -0,0 +0,0 @@ public interface Conversable {
      *
      * @param message Message to be displayed
      */
+    @org.jetbrains.annotations.ApiStatus.Obsolete // Paper
     public void sendRawMessage(@NotNull String message);
 
     /**
@@ -0,0 +0,0 @@ public interface Conversable {
      *
      * @param message Message to be displayed
      * @param sender The sender of this message
+     * @deprecated sender UUID is ignored
      */
+    @Deprecated // Paper
     public void sendRawMessage(@Nullable UUID sender, @NotNull String message);
 }
diff --git a/src/main/java/org/bukkit/enchantments/Enchantment.java b/src/main/java/org/bukkit/enchantments/Enchantment.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/enchantments/Enchantment.java
+++ b/src/main/java/org/bukkit/enchantments/Enchantment.java
@@ -0,0 +0,0 @@ public abstract class Enchantment implements Keyed, Translatable {
      * @return True if the enchantment may be applied, otherwise False
      */
     public abstract boolean canEnchantItem(@NotNull ItemStack item);
+    // Paper start
+    /**
+     * Get the name of the enchantment with its applied level.
+     * <p>
+     * If the given {@code level} is either less than the {@link #getStartLevel()} or greater than the {@link #getMaxLevel()},
+     * the level may not be shown in the numeral format one may otherwise expect.
+     * </p>
+     *
+     * @param level the level of the enchantment to show
+     * @return the name of the enchantment with {@code level} applied
+     */
+    public abstract net.kyori.adventure.text.@NotNull Component displayName(int level);
+    // Paper end
 
     /**
      * Gets the Enchantment at the specified key
diff --git a/src/main/java/org/bukkit/enchantments/EnchantmentWrapper.java b/src/main/java/org/bukkit/enchantments/EnchantmentWrapper.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/enchantments/EnchantmentWrapper.java
+++ b/src/main/java/org/bukkit/enchantments/EnchantmentWrapper.java
@@ -0,0 +0,0 @@ public abstract class EnchantmentWrapper extends Enchantment {
     public Enchantment getEnchantment() {
         return this;
     }
+    // Paper start
+    @NotNull
+    @Override
+    public net.kyori.adventure.text.Component displayName(int level) {
+        return getEnchantment().displayName(level);
+    }
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/entity/Entity.java
+++ b/src/main/java/org/bukkit/entity/Entity.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  * Not all methods are guaranteed to work/may have side effects when
  * {@link #isInWorld()} is false.
  */
-public interface Entity extends Metadatable, CommandSender, Nameable, PersistentDataHolder {
+public interface Entity extends Metadatable, CommandSender, Nameable, PersistentDataHolder, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowEntity>, net.kyori.adventure.sound.Sound.Emitter { // Paper
 
     /**
      * Gets the entity's current position
@@ -0,0 +0,0 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
     @Override
     Spigot spigot();
     // Spigot end
+
+    // Paper start
+    /**
+     * Gets the entity's display name formatted with their team prefix/suffix and
+     * the entity's default hover/click events.
+     *
+     * @return the team display name
+     */
+    net.kyori.adventure.text.@NotNull Component teamDisplayName();
+
+    @NotNull
+    @Override
+    default net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowEntity> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowEntity> op) {
+        return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.customName())));
+    }
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
 /**
  * Represents a player, connected or not
  */
-public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient {
+public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified, net.kyori.adventure.bossbar.BossBarViewer { // Paper
+
+    // Paper start
+    @Override
+    default net.kyori.adventure.identity.@NotNull Identity identity() {
+        return net.kyori.adventure.identity.Identity.identity(this.getUniqueId());
+    }
+
+    /**
+     * Gets an unmodifiable view of all known currently active bossbars.
+     * <p>
+     * <b>This currently only returns bossbars shown to the player via
+     * {@link #showBossBar(net.kyori.adventure.bossbar.BossBar)} and does not contain bukkit
+     * {@link org.bukkit.boss.BossBar} instances shown to the player.</b>
+     *
+     * @return an unmodifiable view of all known currently active bossbars
+     * @since 4.14.0
+     */
+    @Override
+    @org.jetbrains.annotations.UnmodifiableView @NotNull Iterable<? extends net.kyori.adventure.bossbar.BossBar> activeBossBars();
+
+    /**
+     * Gets the "friendly" name to display of this player.
+     *
+     * @return the display name
+     */
+    net.kyori.adventure.text.@NotNull Component displayName();
+
+    /**
+     * Sets the "friendly" name to display of this player.
+     *
+     * @param displayName the display name to set
+     */
+    void displayName(final net.kyori.adventure.text.@Nullable Component displayName);
+    // Paper end
 
     /**
      * {@inheritDoc}
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * places defined by plugins.
      *
      * @return the friendly name
+     * @deprecated in favour of {@link #displayName()}
      */
+    @Deprecated // Paper
     @NotNull
     public String getDisplayName();
 
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * places defined by plugins.
      *
      * @param name The new display name.
+     * @deprecated in favour of {@link #displayName(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setDisplayName(@Nullable String name);
 
+    // Paper start
+    /**
+     * Sets the name that is shown on the in-game player list.
+     * <p>
+     * If the value is null, the name will be identical to {@link #getName()}.
+     *
+     * @param name new player list name
+     */
+    void playerListName(net.kyori.adventure.text.@Nullable Component name);
+
+    /**
+     * Gets the name that is shown on the in-game player list.
+     *
+     * @return the player list name
+     */
+    net.kyori.adventure.text.@NotNull Component playerListName();
+
+    /**
+     * Gets the currently displayed player list header for this player.
+     *
+     * @return player list header or null
+     */
+    net.kyori.adventure.text.@Nullable Component playerListHeader();
+
+    /**
+     * Gets the currently displayed player list footer for this player.
+     *
+     * @return player list footer or null
+     */
+    net.kyori.adventure.text.@Nullable Component playerListFooter();
+    // Paper end
     /**
      * Gets the name that is shown on the player list.
      *
      * @return the player list name
+     * @deprecated in favour of {@link #playerListName()}
      */
     @NotNull
+    @Deprecated // Paper
     public String getPlayerListName();
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * If the value is null, the name will be identical to {@link #getName()}.
      *
      * @param name new player list name
+     * @deprecated in favour of {@link #playerListName(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setPlayerListName(@Nullable String name);
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * Gets the currently displayed player list header for this player.
      *
      * @return player list header or null
+     * @deprecated in favour of {@link #playerListHeader()}
      */
+    @Deprecated // Paper
     @Nullable
     public String getPlayerListHeader();
 
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * Gets the currently displayed player list footer for this player.
      *
      * @return player list header or null
+     * @deprecated in favour of {@link #playerListFooter()}
      */
+    @Deprecated // Paper
     @Nullable
     public String getPlayerListFooter();
 
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * Sets the currently displayed player list header for this player.
      *
      * @param header player list header, null for empty
+     * @deprecated in favour of {@link #sendPlayerListHeader(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setPlayerListHeader(@Nullable String header);
 
     /**
      * Sets the currently displayed player list footer for this player.
      *
      * @param footer player list footer, null for empty
+     * @deprecated in favour of {@link #sendPlayerListFooter(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setPlayerListFooter(@Nullable String footer);
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      *
      * @param header player list header, null for empty
      * @param footer player list footer, null for empty
+     * @deprecated in favour of {@link #sendPlayerListHeaderAndFooter(net.kyori.adventure.text.Component, net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setPlayerListHeaderFooter(@Nullable String header, @Nullable String footer);
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * Kicks player with custom kick message.
      *
      * @param message kick message
+     * @deprecated in favour of {@link #kick(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void kickPlayer(@Nullable String message);
 
+    // Paper start
+    /**
+     * Kicks the player with the default kick message.
+     * @see #kick(net.kyori.adventure.text.Component)
+     */
+    void kick();
+    /**
+     * Kicks player with custom kick message.
+     *
+     * @param message kick message
+     */
+    void kick(final net.kyori.adventure.text.@Nullable Component message);
+    // Paper end
+
     /**
      * Adds this user to the {@link ProfileBanList}. If a previous ban exists, this will
      * update the entry.
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      */
     public void sendEquipmentChange(@NotNull LivingEntity entity, @NotNull Map<EquipmentSlot, ItemStack> items);
 
+    // Paper start
+    /**
+     * Send a sign change. This fakes a sign change packet for a user at
+     * a certain location. This will not actually change the world in any way.
+     * This method will use a sign at the location's block or a faked sign
+     * sent via
+     * {@link #sendBlockChange(org.bukkit.Location, org.bukkit.Material, byte)}.
+     * <p>
+     * If the client does not have a sign at the given location it will
+     * display an error message to the user.
+     *
+     * @param loc the location of the sign
+     * @param lines the new text on the sign or null to clear it
+     * @throws IllegalArgumentException if location is null
+     * @throws IllegalArgumentException if lines is non-null and has a length less than 4
+     * @deprecated Use {@link #sendBlockUpdate(Location, TileState)} by creating a new virtual
+     * {@link org.bukkit.block.Sign} block state via {@link BlockData#createBlockState()}
+     * (constructed e.g. via {@link Material#createBlockData()})
+     */
+    @Deprecated
+    default void sendSignChange(@NotNull Location loc, @Nullable java.util.List<? extends net.kyori.adventure.text.Component> lines) throws IllegalArgumentException {
+        this.sendSignChange(loc, lines, DyeColor.BLACK);
+    }
+
+    /**
+     * Send a sign change. This fakes a sign change packet for a user at
+     * a certain location. This will not actually change the world in any way.
+     * This method will use a sign at the location's block or a faked sign
+     * sent via
+     * {@link #sendBlockChange(org.bukkit.Location, org.bukkit.Material, byte)}.
+     * <p>
+     * If the client does not have a sign at the given location it will
+     * display an error message to the user.
+     *
+     * @param loc the location of the sign
+     * @param lines the new text on the sign or null to clear it
+     * @param dyeColor the color of the sign
+     * @throws IllegalArgumentException if location is null
+     * @throws IllegalArgumentException if dyeColor is null
+     * @throws IllegalArgumentException if lines is non-null and has a length less than 4
+     * @deprecated Use {@link #sendBlockUpdate(Location, TileState)} by creating a new virtual
+     * {@link org.bukkit.block.Sign} block state via {@link BlockData#createBlockState()}
+     * (constructed e.g. via {@link Material#createBlockData()})
+     */
+    @Deprecated
+    default void sendSignChange(@NotNull Location loc, @Nullable java.util.List<? extends net.kyori.adventure.text.Component> lines, @NotNull DyeColor dyeColor) throws IllegalArgumentException {
+        this.sendSignChange(loc, lines, dyeColor, false);
+    }
+
+    /**
+     * Send a sign change. This fakes a sign change packet for a user at
+     * a certain location. This will not actually change the world in any way.
+     * This method will use a sign at the location's block or a faked sign
+     * sent via
+     * {@link #sendBlockChange(org.bukkit.Location, org.bukkit.Material, byte)}.
+     * <p>
+     * If the client does not have a sign at the given location it will
+     * display an error message to the user.
+     *
+     * @param loc the location of the sign
+     * @param lines the new text on the sign or null to clear it
+     * @param hasGlowingText whether the text of the sign should glow as if dyed with a glowing ink sac
+     * @throws IllegalArgumentException if location is null
+     * @throws IllegalArgumentException if dyeColor is null
+     * @throws IllegalArgumentException if lines is non-null and has a length less than 4
+     * @deprecated Use {@link #sendBlockUpdate(Location, TileState)} by creating a new virtual
+     * {@link org.bukkit.block.Sign} block state via {@link BlockData#createBlockState()}
+     * (constructed e.g. via {@link Material#createBlockData()})
+     */
+    @Deprecated
+    default void sendSignChange(@NotNull Location loc, @Nullable java.util.List<? extends net.kyori.adventure.text.Component> lines, boolean hasGlowingText) throws IllegalArgumentException {
+        this.sendSignChange(loc, lines, DyeColor.BLACK, hasGlowingText);
+    }
+
+    /**
+     * Send a sign change. This fakes a sign change packet for a user at
+     * a certain location. This will not actually change the world in any way.
+     * This method will use a sign at the location's block or a faked sign
+     * sent via
+     * {@link #sendBlockChange(org.bukkit.Location, org.bukkit.Material, byte)}.
+     * <p>
+     * If the client does not have a sign at the given location it will
+     * display an error message to the user.
+     *
+     * @param loc the location of the sign
+     * @param lines the new text on the sign or null to clear it
+     * @param dyeColor the color of the sign
+     * @param hasGlowingText whether the text of the sign should glow as if dyed with a glowing ink sac
+     * @throws IllegalArgumentException if location is null
+     * @throws IllegalArgumentException if dyeColor is null
+     * @throws IllegalArgumentException if lines is non-null and has a length less than 4
+     * @deprecated Use {@link #sendBlockUpdate(Location, TileState)} by creating a new virtual
+     * {@link org.bukkit.block.Sign} block state via {@link BlockData#createBlockState()}
+     * (constructed e.g. via {@link Material#createBlockData()})
+     */
+    @Deprecated
+    void sendSignChange(@NotNull Location loc, @Nullable java.util.List<? extends net.kyori.adventure.text.Component> lines, @NotNull DyeColor dyeColor, boolean hasGlowingText)
+        throws IllegalArgumentException;
+    // Paper end
+
     /**
      * Send a sign change. This fakes a sign change packet for a user at
      * a certain location. This will not actually change the world in any way.
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * @param lines the new text on the sign or null to clear it
      * @throws IllegalArgumentException if location is null
      * @throws IllegalArgumentException if lines is non-null and has a length less than 4
+     * @deprecated Use {@link #sendBlockUpdate(Location, TileState)} by creating a new virtual
+     * {@link org.bukkit.block.Sign} block state via {@link BlockData#createBlockState()}
+     * (constructed e.g. via {@link Material#createBlockData()})
      */
+    @Deprecated // Paper
     public void sendSignChange(@NotNull Location loc, @Nullable String[] lines) throws IllegalArgumentException;
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * @throws IllegalArgumentException if location is null
      * @throws IllegalArgumentException if dyeColor is null
      * @throws IllegalArgumentException if lines is non-null and has a length less than 4
+     * @deprecated Use {@link #sendBlockUpdate(Location, TileState)} by creating a new virtual
+     * {@link org.bukkit.block.Sign} block state via {@link BlockData#createBlockState()}
+     * (constructed e.g. via {@link Material#createBlockData()})
      */
+    @Deprecated // Paper
     public void sendSignChange(@NotNull Location loc, @Nullable String[] lines, @NotNull DyeColor dyeColor) throws IllegalArgumentException;
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * @throws IllegalArgumentException if location is null
      * @throws IllegalArgumentException if dyeColor is null
      * @throws IllegalArgumentException if lines is non-null and has a length less than 4
+     * @deprecated Use {@link #sendBlockUpdate(Location, TileState)} by creating a new virtual
+     * {@link org.bukkit.block.Sign} block state via {@link BlockData#createBlockState()}
+     * (constructed e.g. via {@link Material#createBlockData()})
      */
+    @Deprecated // Paper
     public void sendSignChange(@NotNull Location loc, @Nullable String[] lines, @NotNull DyeColor dyeColor, boolean hasGlowingText) throws IllegalArgumentException;
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * @throws IllegalArgumentException Thrown if the URL is null.
      * @throws IllegalArgumentException Thrown if the URL is too long.
      * @deprecated Minecraft no longer uses textures packs. Instead you
-     *     should use {@link #setResourcePack(String)}.
+     *     should use {@link #setResourcePack(UUID, String, byte[], net.kyori.adventure.text.Component, boolean)}.
      */
     @Deprecated
     public void setTexturePack(@NotNull String url);
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * @throws IllegalArgumentException Thrown if the URL is null.
      * @throws IllegalArgumentException Thrown if the URL is too long. The
      *     length restriction is an implementation specific arbitrary value.
+     * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
      */
+    @Deprecated // Paper - adventure
     public void setResourcePack(@NotNull String url);
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      *     pack correctly.
      * </ul>
      *
+     * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
      * @param url The URL from which the client will download the resource
      *     pack. The string must contain only US-ASCII characters and should
      *     be encoded as per RFC 1738.
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
      *     long.
      */
+    @Deprecated // Paper - adventure
     public void setResourcePack(@NotNull String url, @Nullable byte[] hash);
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      *     {@link PlayerResourcePackStatusEvent} to figure out whether or not
      *     the player loaded the pack!
      * <li>To remove a resource pack you can use
-     *     {@link #removeResourcePack(UUID)} or {@link #removeResourcePacks()}.
+     *     {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
      * <li>The request is sent with empty string as the hash when the hash is
      *     not provided. This might result in newer versions not loading the
      *     pack correctly.
      * </ul>
      *
+     * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
      * @param url The URL from which the client will download the resource
      *     pack. The string must contain only US-ASCII characters and should
      *     be encoded as per RFC 1738.
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
      *     long.
      */
+    @Deprecated // Paper - adventure
     public void setResourcePack(@NotNull String url, @Nullable byte[] hash, @Nullable String prompt);
 
+    // Paper start
     /**
      * Request that the player's client download and switch resource packs.
      * <p>
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      *     {@link PlayerResourcePackStatusEvent} to figure out whether or not
      *     the player loaded the pack!
      * <li>To remove a resource pack you can use
-     *     {@link #removeResourcePack(UUID)} or {@link #removeResourcePacks()}.
+     *     {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+     * <li>The request is sent with empty string as the hash when the hash is
+     *     not provided. This might result in newer versions not loading the
+     *     pack correctly.
+     * </ul>
+     *
+     * @param url The URL from which the client will download the resource
+     *     pack. The string must contain only US-ASCII characters and should
+     *     be encoded as per RFC 1738.
+     * @param hash The sha1 hash sum of the resource pack file which is used
+     *     to apply a cached version of the pack directly without downloading
+     *     if it is available. Hast to be 20 bytes long!
+     * @param prompt The optional custom prompt message to be shown to client.
+     * @throws IllegalArgumentException Thrown if the URL is null.
+     * @throws IllegalArgumentException Thrown if the URL is too long. The
+     *     length restriction is an implementation specific arbitrary value.
+     * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
+     *     long.
+     * @see #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)
+     */
+    default void setResourcePack(final @NotNull String url, final byte @Nullable [] hash, final net.kyori.adventure.text.@Nullable Component prompt) {
+        this.setResourcePack(url, hash, prompt, false);
+    }
+    // Paper end
+
+    /**
+     * Request that the player's client download and switch resource packs.
+     * <p>
+     * The player's client will download the new resource pack asynchronously
+     * in the background, and will automatically switch to it once the
+     * download is complete. If the client has downloaded and cached a
+     * resource pack with the same hash in the past it will not download but
+     * directly apply the cached pack. If the hash is null and the client has
+     * downloaded and cached the same resource pack in the past, it will
+     * perform a file size check against the response content to determine if
+     * the resource pack has changed and needs to be downloaded again. When
+     * this request is sent for the very first time from a given server, the
+     * client will first display a confirmation GUI to the player before
+     * proceeding with the download.
+     * <p>
+     * Notes:
+     * <ul>
+     * <li>Players can disable server resources on their client, in which
+     *     case this method will have no affect on them. Use the
+     *     {@link PlayerResourcePackStatusEvent} to figure out whether or not
+     *     the player loaded the pack!
+     * <li>To remove a resource pack you can use
+     *     {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
      * <li>The request is sent with empty string as the hash when the hash is
      *     not provided. This might result in newer versions not loading the
      *     pack correctly.
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      *     length restriction is an implementation specific arbitrary value.
      * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
      *     long.
+     * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
      */
+    @Deprecated // Paper - adventure
     public void setResourcePack(@NotNull String url, @Nullable byte[] hash, boolean force);
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      *     {@link PlayerResourcePackStatusEvent} to figure out whether or not
      *     the player loaded the pack!
      * <li>To remove a resource pack you can use
-     *     {@link #removeResourcePack(UUID)} or {@link #removeResourcePacks()}.
+     *     {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
      * <li>The request is sent with empty string as the hash when the hash is
      *     not provided. This might result in newer versions not loading the
      *     pack correctly.
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      *     length restriction is an implementation specific arbitrary value.
      * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
      *     long.
+     * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
      */
+    @Deprecated // Paper
     public void setResourcePack(@NotNull String url, @Nullable byte[] hash, @Nullable String prompt, boolean force);
 
+    // Paper start
+    /**
+     * Request that the player's client download and switch resource packs.
+     * <p>
+     * The player's client will download the new resource pack asynchronously
+     * in the background, and will automatically switch to it once the
+     * download is complete. If the client has downloaded and cached a
+     * resource pack with the same hash in the past it will not download but
+     * directly apply the cached pack. If the hash is null and the client has
+     * downloaded and cached the same resource pack in the past, it will
+     * perform a file size check against the response content to determine if
+     * the resource pack has changed and needs to be downloaded again. When
+     * this request is sent for the very first time from a given server, the
+     * client will first display a confirmation GUI with a custom prompt
+     * to the player before proceeding with the download.
+     * <p>
+     * Notes:
+     * <ul>
+     * <li>Players can disable server resources on their client, in which
+     *     case this method will have no affect on them. Use the
+     *     {@link PlayerResourcePackStatusEvent} to figure out whether or not
+     *     the player loaded the pack!
+     * <li>To remove a resource pack you can use
+     *     {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+     * <li>The request is sent with empty string as the hash when the hash is
+     *     not provided. This might result in newer versions not loading the
+     *     pack correctly.
+     * </ul>
+     *
+     * @param url The URL from which the client will download the resource
+     *     pack. The string must contain only US-ASCII characters and should
+     *     be encoded as per RFC 1738.
+     * @param hash The sha1 hash sum of the resource pack file which is used
+     *     to apply a cached version of the pack directly without downloading
+     *     if it is available. Hast to be 20 bytes long!
+     * @param prompt The optional custom prompt message to be shown to client.
+     * @param force If true, the client will be disconnected from the server
+     *     when it declines to use the resource pack.
+     * @throws IllegalArgumentException Thrown if the URL is null.
+     * @throws IllegalArgumentException Thrown if the URL is too long. The
+     *     length restriction is an implementation specific arbitrary value.
+     * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
+     *     long.
+     * @see #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)
+     */
+    default void setResourcePack(final @NotNull String url, final byte @Nullable [] hash, final net.kyori.adventure.text.@Nullable Component prompt, final boolean force) {
+        this.setResourcePack(UUID.nameUUIDFromBytes(url.getBytes(java.nio.charset.StandardCharsets.UTF_8)), url, hash, prompt, force);
+    }
+    // Paper end
+
     /**
      * Request that the player's client download and switch resource packs.
      * <p>
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      *     {@link PlayerResourcePackStatusEvent} to figure out whether or not
      *     the player loaded the pack!
      * <li>To remove a resource pack you can use
-     *     {@link #removeResourcePack(UUID)} or {@link #removeResourcePacks()}.
+     *     {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
      * <li>The request is sent with empty string as the hash when the hash is
      *     not provided. This might result in newer versions not loading the
      *     pack correctly.
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      *     length restriction is an implementation specific arbitrary value.
      * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
      *     long.
+     * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
      */
+    @Deprecated // Paper - adventure
     public void setResourcePack(@NotNull UUID id, @NotNull String url, @Nullable byte[] hash, @Nullable String prompt, boolean force);
 
+    // Paper start
+    /**
+     * Request that the player's client download and switch resource packs.
+     * <p>
+     * The player's client will download the new resource pack asynchronously
+     * in the background, and will automatically switch to it once the
+     * download is complete. If the client has downloaded and cached a
+     * resource pack with the same hash in the past it will not download but
+     * directly apply the cached pack. If the hash is null and the client has
+     * downloaded and cached the same resource pack in the past, it will
+     * perform a file size check against the response content to determine if
+     * the resource pack has changed and needs to be downloaded again. When
+     * this request is sent for the very first time from a given server, the
+     * client will first display a confirmation GUI to the player before
+     * proceeding with the download.
+     * <p>
+     * Notes:
+     * <ul>
+     * <li>Players can disable server resources on their client, in which
+     *     case this method will have no affect on them. Use the
+     *     {@link PlayerResourcePackStatusEvent} to figure out whether or not
+     *     the player loaded the pack!
+     * <li>To remove a resource pack you can use
+     *     {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+     * <li>The request is sent with empty string as the hash when the hash is
+     *     not provided. This might result in newer versions not loading the
+     *     pack correctly.
+     * </ul>
+     *
+     * @param uuid Unique resource pack ID.
+     * @param url The URL from which the client will download the resource
+     *     pack. The string must contain only US-ASCII characters and should
+     *     be encoded as per RFC 1738.
+     * @param hash The sha1 hash sum of the resource pack file which is used
+     *     to apply a cached version of the pack directly without downloading
+     *     if it is available. Hast to be 20 bytes long!
+     * @param prompt The optional custom prompt message to be shown to client.
+     * @param force If true, the client will be disconnected from the server
+     *     when it declines to use the resource pack.
+     * @throws IllegalArgumentException Thrown if the URL is null.
+     * @throws IllegalArgumentException Thrown if the URL is too long. The
+     *     length restriction is an implementation specific arbitrary value.
+     * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
+     *     long.
+     * @see #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)
+     */
+    void setResourcePack(@NotNull UUID uuid, @NotNull String url, byte @Nullable [] hash, net.kyori.adventure.text.@Nullable Component prompt, boolean force);
+    // Paper end
+
     /**
      * Request that the player's client download and include another resource pack.
      * <p>
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      *
      * @param id the id of the resource pack.
      * @throws IllegalArgumentException If the ID is null.
+     * @see #removeResourcePacks(UUID, UUID...)
      */
     public void removeResourcePack(@NotNull UUID id);
 
     /**
      * Request that the player's client remove all loaded resource pack sent by
      * the server.
+     * @see #clearResourcePacks()
      */
     public void removeResourcePacks();
 
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      *
      * @param title Title text
      * @param subtitle Subtitle text
-     * @deprecated API behavior subject to change
+     * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
      */
     @Deprecated
     public void sendTitle(@Nullable String title, @Nullable String subtitle);
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * @param fadeIn time in ticks for titles to fade in. Defaults to 10.
      * @param stay time in ticks for titles to stay. Defaults to 70.
      * @param fadeOut time in ticks for titles to fade out. Defaults to 20.
+     * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
      */
+    @Deprecated // Paper - Adventure
     public void sendTitle(@Nullable String title, @Nullable String subtitle, int fadeIn, int stay, int fadeOut);
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      */
     public int getClientViewDistance();
 
+    // Paper start
+    /**
+     * Gets the player's current locale.
+     *
+     * @return the player's locale
+     */
+    @NotNull java.util.Locale locale();
+    // Paper end
     /**
      * Gets the player's estimated ping in milliseconds.
      *
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      * they wish.
      *
      * @return the player's locale
+     * @deprecated in favour of {@link #locale()}
      */
     @NotNull
+    @Deprecated // Paper
     public String getLocale();
 
     /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
      */
     public boolean isAllowingServerListings();
 
+    // Paper start
+    @NotNull
+    @Override
+    default net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowEntity> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowEntity> op) {
+        return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.displayName())));
+    }
+    // Paper end
+
     // Spigot start
     public class Spigot extends Entity.Spigot {
 
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
             throw new UnsupportedOperationException("Not supported yet.");
         }
 
+        @Deprecated // Paper
         @Override
         public void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
 
+        @Deprecated // Paper
         @Override
         public void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
             throw new UnsupportedOperationException("Not supported yet.");
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
          *
          * @param position the screen position
          * @param component the components to send
+         * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
          */
+        @Deprecated // Paper
         public void sendMessage(@NotNull net.md_5.bungee.api.ChatMessageType position, @NotNull net.md_5.bungee.api.chat.BaseComponent component) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
          *
          * @param position the screen position
          * @param components the components to send
+         * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
          */
+        @Deprecated // Paper
         public void sendMessage(@NotNull net.md_5.bungee.api.ChatMessageType position, @NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
          * @param position the screen position
          * @param sender the sender of the message
          * @param component the components to send
+         * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
          */
+        @Deprecated // Paper
         public void sendMessage(@NotNull net.md_5.bungee.api.ChatMessageType position, @Nullable java.util.UUID sender, @NotNull net.md_5.bungee.api.chat.BaseComponent component) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
          * @param position the screen position
          * @param sender the sender of the message
          * @param components the components to send
+         * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
          */
+        @Deprecated // Paper
         public void sendMessage(@NotNull net.md_5.bungee.api.ChatMessageType position, @Nullable java.util.UUID sender, @NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
diff --git a/src/main/java/org/bukkit/entity/TextDisplay.java b/src/main/java/org/bukkit/entity/TextDisplay.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/entity/TextDisplay.java
+++ b/src/main/java/org/bukkit/entity/TextDisplay.java
@@ -0,0 +0,0 @@ public interface TextDisplay extends Display {
      * Gets the displayed text.
      *
      * @return the displayed text.
+     * @deprecated in favour of {@link #text()}
      */
     @Nullable
+    @Deprecated // Paper
     String getText();
 
     /**
      * Sets the displayed text.
      *
      * @param text the new text
+     * @deprecated in favour of {@link #text(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     void setText(@Nullable String text);
 
+    // Paper start
+    /**
+     * Gets the displayed text.
+     *
+     * @return the displayed text
+     */
+    net.kyori.adventure.text.@NotNull Component text();
+
+    /**
+     * Sets the displayed text.
+     *
+     * @param text the new text
+     */
+    void text(net.kyori.adventure.text.@Nullable Component text);
+    // Paper end
+
     /**
      * Gets the maximum line width before wrapping.
      *
diff --git a/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java b/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java
+++ b/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java
@@ -0,0 +0,0 @@ public interface CommandMinecart extends Minecart {
      * same as setting it to "@".
      *
      * @param name New name for this CommandMinecart.
+     * @deprecated in favour of {@link #customName(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setName(@Nullable String name);
 
 }
diff --git a/src/main/java/org/bukkit/event/block/SignChangeEvent.java b/src/main/java/org/bukkit/event/block/SignChangeEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/block/SignChangeEvent.java
+++ b/src/main/java/org/bukkit/event/block/SignChangeEvent.java
@@ -0,0 +0,0 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
     private static final HandlerList handlers = new HandlerList();
     private boolean cancel = false;
     private final Player player;
-    private final String[] lines;
+    private final java.util.List<net.kyori.adventure.text.Component> adventure$lines; // Paper
     private final Side side;
 
+    // Paper start
+    public SignChangeEvent(@NotNull final Block theBlock, @NotNull final Player player, @NotNull final java.util.List<net.kyori.adventure.text.Component> adventure$lines, @NotNull Side side) {
+        super(theBlock);
+        this.player = player;
+        this.adventure$lines = adventure$lines;
+        this.side = side;
+    }
+
+    @Deprecated
+    public SignChangeEvent(@NotNull final Block theBlock, @NotNull final Player player, @NotNull final java.util.List<net.kyori.adventure.text.Component> adventure$lines) {
+        this(theBlock, player, adventure$lines, Side.FRONT);
+    }
+    // Paper end
+
     @Deprecated
     public SignChangeEvent(@NotNull final Block theBlock, @NotNull final Player thePlayer, @NotNull final String[] theLines) {
         this(theBlock, thePlayer, theLines, Side.FRONT);
     }
 
+    @Deprecated // Paper
     public SignChangeEvent(@NotNull final Block theBlock, @NotNull final Player thePlayer, @NotNull final String[] theLines, @NotNull Side side) {
         super(theBlock);
         this.player = thePlayer;
-        this.lines = theLines;
+        // Paper start
+        this.adventure$lines = new java.util.ArrayList<>();
+        for (String theLine : theLines) {
+            this.adventure$lines.add(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(theLine));
+        }
+        // Paper end
         this.side = side;
     }
 
@@ -0,0 +0,0 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
         return player;
     }
 
+    // Paper start
+    /**
+     * Gets all of the lines of text from the sign involved in this event.
+     *
+     * @return the String array for the sign's lines new text
+     */
+    public @NotNull java.util.List<net.kyori.adventure.text.Component> lines() {
+        return this.adventure$lines;
+    }
+
+    /**
+     * Gets a single line of text from the sign involved in this event.
+     *
+     * @param index index of the line to get
+     * @return the String containing the line of text associated with the
+     *     provided index
+     * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
+     *     or < 0}
+     */
+    public net.kyori.adventure.text.@Nullable Component line(int index) throws IndexOutOfBoundsException {
+        return this.adventure$lines.get(index);
+    }
+
+    /**
+     * Sets a single line for the sign involved in this event
+     *
+     * @param index index of the line to set
+     * @param line text to set
+     * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
+     *     or < 0}
+     */
+    public void line(int index, net.kyori.adventure.text.@Nullable Component line) throws IndexOutOfBoundsException {
+        this.adventure$lines.set(index, line);
+    }
+    // Paper end
+
     /**
      * Gets all of the lines of text from the sign involved in this event.
      *
      * @return the String array for the sign's lines new text
+     * @deprecated in favour of {@link #lines()}
      */
     @NotNull
+    @Deprecated // Paper
     public String[] getLines() {
-        return lines;
+        return adventure$lines.stream().map(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection()::serialize).toArray(String[]::new); // Paper
     }
 
     /**
@@ -0,0 +0,0 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
      *     provided index
      * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
      *     or < 0}
+     * @deprecated in favour of {@link #line(int)}
      */
     @Nullable
+    @Deprecated // Paper
     public String getLine(int index) throws IndexOutOfBoundsException {
-        return lines[index];
+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.adventure$lines.get(index)); // Paper
     }
 
     /**
@@ -0,0 +0,0 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
      * @param line text to set
      * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
      *     or < 0}
+     * @deprecated in favour of {@link #line(int, net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setLine(int index, @Nullable String line) throws IndexOutOfBoundsException {
-        lines[index] = line;
+        adventure$lines.set(index, line != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(line) : null); // Paper
     }
 
     /**
diff --git a/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java b/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
+++ b/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  */
 public class PlayerDeathEvent extends EntityDeathEvent {
     private int newExp = 0;
-    private String deathMessage = "";
+    private net.kyori.adventure.text.Component deathMessage; // Paper - adventure
     private int newLevel = 0;
     private int newTotalExp = 0;
     private boolean keepLevel = false;
     private boolean keepInventory = false;
+    // Paper start - adventure
+    @org.jetbrains.annotations.ApiStatus.Internal
+    public PlayerDeathEvent(final @NotNull Player player, final @NotNull DamageSource damageSource, final @NotNull List<ItemStack> drops, final int droppedExp, final @Nullable net.kyori.adventure.text.Component deathMessage) {
+        this(player, damageSource, drops, droppedExp, 0, deathMessage);
+    }
+
+    @org.jetbrains.annotations.ApiStatus.Internal
+    public PlayerDeathEvent(final @NotNull Player player, final @NotNull DamageSource damageSource, final @NotNull List<ItemStack> drops, final int droppedExp, final int newExp, final @Nullable net.kyori.adventure.text.Component deathMessage) {
+        this(player, damageSource, drops, droppedExp, newExp, 0, 0, deathMessage);
+    }
+
+    @org.jetbrains.annotations.ApiStatus.Internal
+    public PlayerDeathEvent(final @NotNull Player player, final @NotNull DamageSource damageSource, final @NotNull List<ItemStack> drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, final @Nullable net.kyori.adventure.text.Component deathMessage) {
+        super(player, damageSource, drops, droppedExp);
+        this.newExp = newExp;
+        this.newTotalExp = newTotalExp;
+        this.newLevel = newLevel;
+        this.deathMessage = deathMessage;
+    }
+    // Paper end - adventure
 
+    @Deprecated // Paper
     public PlayerDeathEvent(@NotNull final Player player, @NotNull DamageSource damageSource, @NotNull final List<ItemStack> drops, final int droppedExp, @Nullable final String deathMessage) {
         this(player, damageSource, drops, droppedExp, 0, deathMessage);
     }
 
+    @Deprecated // Paper
     public PlayerDeathEvent(@NotNull final Player player, @NotNull DamageSource damageSource, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, @Nullable final String deathMessage) {
         this(player, damageSource, drops, droppedExp, newExp, 0, 0, deathMessage);
     }
 
+    @Deprecated // Paper
     public PlayerDeathEvent(@NotNull final Player player, @NotNull DamageSource damageSource, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, @Nullable final String deathMessage) {
         super(player, damageSource, drops, droppedExp);
         this.newExp = newExp;
         this.newTotalExp = newTotalExp;
         this.newLevel = newLevel;
-        this.deathMessage = deathMessage;
+        this.deathMessage = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserializeOrNull(deathMessage); // Paper
     }
 
     @NotNull
@@ -0,0 +0,0 @@ public class PlayerDeathEvent extends EntityDeathEvent {
         return (Player) entity;
     }
 
+    // Paper start - adventure
+    /**
+     * Set the death message that will appear to everyone on the server.
+     *
+     * @param deathMessage Component message to appear to other players on the server.
+     */
+    public void deathMessage(final net.kyori.adventure.text.@Nullable Component deathMessage) {
+        this.deathMessage = deathMessage;
+    }
+
+    /**
+     * Get the death message that will appear to everyone on the server.
+     *
+     * @return Component message to appear to other players on the server.
+     */
+    public net.kyori.adventure.text.@Nullable Component deathMessage() {
+        return this.deathMessage;
+    }
+    // Paper end - adventure
+
     /**
      * Set the death message that will appear to everyone on the server.
      *
      * @param deathMessage Message to appear to other players on the server.
+     * @deprecated in favour of {@link #deathMessage(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setDeathMessage(@Nullable String deathMessage) {
-        this.deathMessage = deathMessage;
+        this.deathMessage = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserializeOrNull(deathMessage); // Paper
     }
 
     /**
      * Get the death message that will appear to everyone on the server.
      *
      * @return Message to appear to other players on the server.
+     * @deprecated in favour of {@link #deathMessage()}
      */
     @Nullable
+    @Deprecated // Paper
     public String getDeathMessage() {
-        return deathMessage;
+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serializeOrNull(this.deathMessage); // Paper
     }
-
+    // Paper end
     /**
      * Gets how much EXP the Player should have at respawn.
      * <p>
diff --git a/src/main/java/org/bukkit/event/inventory/InventoryType.java b/src/main/java/org/bukkit/event/inventory/InventoryType.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/inventory/InventoryType.java
+++ b/src/main/java/org/bukkit/event/inventory/InventoryType.java
@@ -0,0 +0,0 @@ public enum InventoryType {
     private final String title;
     private final MenuType menuType;
     private final boolean isCreatable;
+    // Paper start
+    private final net.kyori.adventure.text.Component defaultTitleComponent;
 
+    /**
+     * Gets the inventory's default title.
+     *
+     * @return the inventory's default title
+     */
+    public net.kyori.adventure.text.@NotNull Component defaultTitle() {
+        return defaultTitleComponent;
+    }
+    // Paper end
     private InventoryType(int defaultSize, /*@NotNull*/ String defaultTitle, @Nullable MenuType type) {
         this(defaultSize, defaultTitle, type, true);
     }
@@ -0,0 +0,0 @@ public enum InventoryType {
         title = defaultTitle;
         this.menuType = type;
         this.isCreatable = isCreatable;
+        this.defaultTitleComponent = net.kyori.adventure.text.Component.text(defaultTitle); // Paper - Adventure
     }
 
     public int getDefaultSize() {
@@ -0,0 +0,0 @@ public enum InventoryType {
     }
 
     @NotNull
+    @Deprecated // Paper
     public String getDefaultTitle() {
         return title;
     }
diff --git a/src/main/java/org/bukkit/event/player/AsyncPlayerChatEvent.java b/src/main/java/org/bukkit/event/player/AsyncPlayerChatEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/player/AsyncPlayerChatEvent.java
+++ b/src/main/java/org/bukkit/event/player/AsyncPlayerChatEvent.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
  * <p>
  * Care should be taken to check {@link #isAsynchronous()} and treat the event
  * appropriately.
+ *
+ * @deprecated use {@link io.papermc.paper.event.player.AsyncChatEvent} instead
  */
+@Deprecated // Paper
+@org.bukkit.Warning(value = false, reason = "Don't nag on old event yet") // Paper
 public class AsyncPlayerChatEvent extends PlayerEvent implements Cancellable {
     private static final HandlerList handlers = new HandlerList();
     private boolean cancel = false;
diff --git a/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java b/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
+++ b/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
  * Stores details for players attempting to log in.
  * <p>
  * This event is asynchronous, and not run using main thread.
+ * <p>
+ * When this event is fired, the player's locale is not
+ * available. Therefore, any translatable component will be
+ * rendered with the default locale, {@link java.util.Locale#US}.
+ * <p>
+ * Consider rendering any translatable yourself with {@link net.kyori.adventure.translation.GlobalTranslator#render}
+ * if the client's language is known.
  */
 public class AsyncPlayerPreLoginEvent extends Event {
     private static final HandlerList handlers = new HandlerList();
     private Result result;
-    private String message;
+    private net.kyori.adventure.text.Component message; // Paper
     private final String name;
     private final InetAddress ipAddress;
     private final UUID uniqueId;
@@ -0,0 +0,0 @@ public class AsyncPlayerPreLoginEvent extends Event {
     public AsyncPlayerPreLoginEvent(@NotNull final String name, @NotNull final InetAddress ipAddress, @NotNull final UUID uniqueId, boolean transferred) {
         super(true);
         this.result = Result.ALLOWED;
-        this.message = "";
+        this.message = net.kyori.adventure.text.Component.empty(); // Paper
         this.name = name;
         this.ipAddress = ipAddress;
         this.uniqueId = uniqueId;
@@ -0,0 +0,0 @@ public class AsyncPlayerPreLoginEvent extends Event {
         this.result = result == null ? null : Result.valueOf(result.name());
     }
 
+    // Paper start
     /**
      * Gets the current kick message that will be used if getResult() !=
      * Result.ALLOWED
@@ -0,0 +0,0 @@ public class AsyncPlayerPreLoginEvent extends Event {
      * @return Current kick message
      */
     @NotNull
-    public String getKickMessage() {
+    public net.kyori.adventure.text.Component kickMessage() {
         return message;
     }
 
@@ -0,0 +0,0 @@ public class AsyncPlayerPreLoginEvent extends Event {
      *
      * @param message New kick message
      */
-    public void setKickMessage(@NotNull final String message) {
+    public void kickMessage(@NotNull final net.kyori.adventure.text.Component message) {
+        this.message = message;
+    }
+
+    /**
+     * Disallows the player from logging in, with the given reason
+     *
+     * @param result New result for disallowing the player
+     * @param message Kick message to display to the user
+     */
+    public void disallow(@NotNull final Result result, @NotNull final net.kyori.adventure.text.Component message) {
+        this.result = result;
         this.message = message;
     }
 
+    /**
+     * Disallows the player from logging in, with the given reason
+     *
+     * @param result New result for disallowing the player
+     * @param message Kick message to display to the user
+     * @deprecated This method uses a deprecated enum from {@link
+     *     PlayerPreLoginEvent}
+     * @see #disallow(Result, String)
+     */
+    @Deprecated
+    public void disallow(@NotNull final PlayerPreLoginEvent.Result result, @NotNull final net.kyori.adventure.text.Component message) {
+        this.result = result == null ? null : Result.valueOf(result.name());
+        this.message = message;
+    }
+    // Paper end
+    /**
+     * Gets the current kick message that will be used if getResult() !=
+     * Result.ALLOWED
+     *
+     * @return Current kick message
+     * @deprecated in favour of {@link #kickMessage()}
+     */
+    @NotNull
+    @Deprecated // Paper
+    public String getKickMessage() {
+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.message); // Paper
+    }
+
+    /**
+     * Sets the kick message to display if getResult() != Result.ALLOWED
+     *
+     * @param message New kick message
+     * @deprecated in favour of {@link #kickMessage(net.kyori.adventure.text.Component)}
+     */
+    @Deprecated // Paper
+    public void setKickMessage(@NotNull final String message) {
+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
+    }
+
     /**
      * Allows the player to log in
      */
     public void allow() {
         result = Result.ALLOWED;
-        message = "";
+        message = net.kyori.adventure.text.Component.empty(); // Paper
     }
 
     /**
@@ -0,0 +0,0 @@ public class AsyncPlayerPreLoginEvent extends Event {
      *
      * @param result New result for disallowing the player
      * @param message Kick message to display to the user
+     * @deprecated in favour of {@link #disallow(org.bukkit.event.player.AsyncPlayerPreLoginEvent.Result, net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void disallow(@NotNull final Result result, @NotNull final String message) {
         this.result = result;
-        this.message = message;
+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
     }
 
     /**
@@ -0,0 +0,0 @@ public class AsyncPlayerPreLoginEvent extends Event {
     @Deprecated
     public void disallow(@NotNull final PlayerPreLoginEvent.Result result, @NotNull final String message) {
         this.result = result == null ? null : Result.valueOf(result.name());
-        this.message = message;
+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
     }
 
     /**
diff --git a/src/main/java/org/bukkit/event/player/PlayerChatEvent.java b/src/main/java/org/bukkit/event/player/PlayerChatEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/player/PlayerChatEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerChatEvent.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
 /**
  * Holds information for player chat and commands
  *
- * @deprecated This event will fire from the main thread and allows the use of
- *     all of the Bukkit API, unlike the {@link AsyncPlayerChatEvent}.
- *     <p>
- *     Listening to this event forces chat to wait for the main thread which
- *     causes delays for chat. {@link AsyncPlayerChatEvent} is the encouraged
- *     alternative for thread safe implementations.
+ * @deprecated Listening to this event forces chat to wait for the main thread, delaying chat messages. It is recommended to use {@link io.papermc.paper.event.player.AsyncChatEvent} instead, wherever possible.
  */
 @Deprecated
 @Warning(reason = "Listening to this event forces chat to wait for the main thread, delaying chat messages.")
diff --git a/src/main/java/org/bukkit/event/player/PlayerEvent.java b/src/main/java/org/bukkit/event/player/PlayerEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/player/PlayerEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerEvent.java
@@ -0,0 +0,0 @@ public abstract class PlayerEvent extends Event {
         player = who;
     }
 
-    PlayerEvent(@NotNull final Player who, boolean async) {
+    public PlayerEvent(@NotNull final Player who, boolean async) { // Paper - public
         super(async);
         player = who;
 
diff --git a/src/main/java/org/bukkit/event/player/PlayerJoinEvent.java b/src/main/java/org/bukkit/event/player/PlayerJoinEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/player/PlayerJoinEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerJoinEvent.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  */
 public class PlayerJoinEvent extends PlayerEvent {
     private static final HandlerList handlers = new HandlerList();
-    private String joinMessage;
+    // Paper start
+    private net.kyori.adventure.text.Component joinMessage;
+    public PlayerJoinEvent(@NotNull final Player playerJoined, @Nullable final net.kyori.adventure.text.Component joinMessage) {
+        super(playerJoined);
+        this.joinMessage = joinMessage;
+    }
 
+    @Deprecated // Paper end
     public PlayerJoinEvent(@NotNull final Player playerJoined, @Nullable final String joinMessage) {
         super(playerJoined);
+        this.joinMessage = joinMessage != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(joinMessage) : null; // Paper end
+    }
+
+    // Paper start
+    /**
+     * Gets the join message to send to all online players
+     *
+     * @return string join message. Can be null
+     */
+    public net.kyori.adventure.text.@Nullable Component joinMessage() {
+        return this.joinMessage;
+    }
+
+    /**
+     * Sets the join message to send to all online players
+     *
+     * @param joinMessage join message. If null, no message will be sent
+     */
+    public void joinMessage(net.kyori.adventure.text.@Nullable Component joinMessage) {
         this.joinMessage = joinMessage;
     }
+    // Paper end
 
     /**
      * Gets the join message to send to all online players
      *
      * @return string join message. Can be null
+     * @deprecated in favour of {@link #joinMessage()}
      */
     @Nullable
+    @Deprecated // Paper
     public String getJoinMessage() {
-        return joinMessage;
+        return this.joinMessage == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.joinMessage); // Paper
     }
 
     /**
      * Sets the join message to send to all online players
      *
      * @param joinMessage join message. If null, no message will be sent
+     * @deprecated in favour of {@link #joinMessage(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setJoinMessage(@Nullable String joinMessage) {
-        this.joinMessage = joinMessage;
+        this.joinMessage = joinMessage != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(joinMessage) : null; // Paper
     }
 
     @NotNull
diff --git a/src/main/java/org/bukkit/event/player/PlayerKickEvent.java b/src/main/java/org/bukkit/event/player/PlayerKickEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/player/PlayerKickEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerKickEvent.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
  */
 public class PlayerKickEvent extends PlayerEvent implements Cancellable {
     private static final HandlerList handlers = new HandlerList();
-    private String leaveMessage;
-    private String kickReason;
+    private net.kyori.adventure.text.Component leaveMessage; // Paper
+    private net.kyori.adventure.text.Component kickReason; // Paper
     private boolean cancel;
 
+    @Deprecated // Paper
     public PlayerKickEvent(@NotNull final Player playerKicked, @NotNull final String kickReason, @NotNull final String leaveMessage) {
+        super(playerKicked);
+        this.kickReason = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(kickReason); // Paper
+        this.leaveMessage = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(leaveMessage); // Paper
+        this.cancel = false;
+    }
+    // Paper start
+    public PlayerKickEvent(@NotNull final Player playerKicked, @NotNull final net.kyori.adventure.text.Component kickReason, @NotNull final net.kyori.adventure.text.Component leaveMessage) {
         super(playerKicked);
         this.kickReason = kickReason;
         this.leaveMessage = leaveMessage;
         this.cancel = false;
     }
 
+    /**
+     * Gets the leave message send to all online players
+     *
+     * @return string kick reason
+     */
+    public net.kyori.adventure.text.@NotNull Component leaveMessage() {
+        return this.leaveMessage;
+    }
+
+    /**
+     * Sets the leave message send to all online players
+     *
+     * @param leaveMessage leave message
+     */
+    public void leaveMessage(net.kyori.adventure.text.@NotNull Component leaveMessage) {
+        this.leaveMessage = leaveMessage;
+    }
+
     /**
      * Gets the reason why the player is getting kicked
      *
      * @return string kick reason
      */
+    public net.kyori.adventure.text.@NotNull Component reason() {
+        return this.kickReason;
+    }
+
+    /**
+     * Sets the reason why the player is getting kicked
+     *
+     * @param kickReason kick reason
+     */
+    public void reason(net.kyori.adventure.text.@NotNull Component kickReason) {
+        this.kickReason = kickReason;
+    }
+    // Paper end
+
+    /**
+     * Gets the reason why the player is getting kicked
+     *
+     * @return string kick reason
+     * @deprecated in favour of {@link #reason()}
+     */
     @NotNull
+    @Deprecated // Paper
     public String getReason() {
-        return kickReason;
+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.kickReason); // Paper
     }
 
     /**
      * Gets the leave message send to all online players
      *
      * @return string kick reason
+     * @deprecated in favour of {@link #leaveMessage()}
      */
     @NotNull
+    @Deprecated // Paper
     public String getLeaveMessage() {
-        return leaveMessage;
+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.leaveMessage); // Paper
     }
 
     @Override
@@ -0,0 +0,0 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
      * Sets the reason why the player is getting kicked
      *
      * @param kickReason kick reason
+     * @deprecated in favour of {@link #reason(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setReason(@NotNull String kickReason) {
-        this.kickReason = kickReason;
+        this.kickReason = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(kickReason); // Paper
     }
 
     /**
      * Sets the leave message send to all online players
      *
      * @param leaveMessage leave message
+     * @deprecated in favour of {@link #leaveMessage(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setLeaveMessage(@NotNull String leaveMessage) {
-        this.leaveMessage = leaveMessage;
+        this.leaveMessage = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(leaveMessage); // Paper
     }
 
     @NotNull
diff --git a/src/main/java/org/bukkit/event/player/PlayerLocaleChangeEvent.java b/src/main/java/org/bukkit/event/player/PlayerLocaleChangeEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/player/PlayerLocaleChangeEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerLocaleChangeEvent.java
@@ -0,0 +0,0 @@ public class PlayerLocaleChangeEvent extends PlayerEvent {
     private static final HandlerList handlers = new HandlerList();
     //
     private final String locale;
+    // Paper start
+    private final java.util.Locale adventure$locale;
+    /**
+     * @see Player#locale()
+     *
+     * @return the player's new locale
+     */
+    public @NotNull java.util.Locale locale() {
+        return this.adventure$locale;
+    }
+    // Paper end
 
     public PlayerLocaleChangeEvent(@NotNull Player who, @NotNull String locale) {
         super(who);
         this.locale = locale;
+        this.adventure$locale = java.util.Objects.requireNonNullElse(net.kyori.adventure.translation.Translator.parseLocale(locale), java.util.Locale.US); // Paper start
     }
 
     /**
      * @return the player's new locale
      * @see Player#getLocale()
+     * @deprecated in favour of {@link #locale()}
      */
     @NotNull
+    @Deprecated // Paper
     public String getLocale() {
         return locale;
     }
diff --git a/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java b/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java
@@ -0,0 +0,0 @@ public class PlayerLoginEvent extends PlayerEvent {
     private final InetAddress realAddress;
     private final String hostname;
     private Result result = Result.ALLOWED;
-    private String message = "";
+    private net.kyori.adventure.text.Component message = net.kyori.adventure.text.Component.empty();
 
     /**
      * This constructor defaults message to an empty string, and result to
@@ -0,0 +0,0 @@ public class PlayerLoginEvent extends PlayerEvent {
      * @param result The result status for this event
      * @param message The message to be displayed if result denies login
      * @param realAddress the actual, unspoofed connecting address
+     * @deprecated in favour of {@link #PlayerLoginEvent(Player, String, InetAddress, Result, net.kyori.adventure.text.Component, InetAddress)}
      */
+    @Deprecated // Paper
     public PlayerLoginEvent(@NotNull final Player player, @NotNull String hostname, @NotNull final InetAddress address, @NotNull final Result result, @NotNull final String message, @NotNull final InetAddress realAddress) {
         this(player, hostname, address, realAddress);
         this.result = result;
+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
+    }
+
+    // Paper start
+    /**
+     * This constructor pre-configures the event with a result and message
+     *
+     * @param player The {@link Player} for this event
+     * @param hostname The hostname that was used to connect to the server
+     * @param address The address the player used to connect, provided for
+     *     timing issues
+     * @param result The result status for this event
+     * @param message The message to be displayed if result denies login
+     * @param realAddress the actual, unspoofed connecting address
+     */
+    public PlayerLoginEvent(@NotNull final Player player, @NotNull String hostname, @NotNull final InetAddress address, @NotNull final Result result, @NotNull final net.kyori.adventure.text.Component message, @NotNull final InetAddress realAddress) {
+        this(player, hostname, address, realAddress); // Spigot
+        this.result = result;
         this.message = message;
     }
 
+    /**
+     * Gets the current kick message that will be used if getResult() !=
+     * Result.ALLOWED
+     *
+     * @return Current kick message
+     */
+    public net.kyori.adventure.text.@NotNull Component kickMessage() {
+        return this.message;
+    }
+
+    /**
+     * Sets the kick message to display if getResult() != Result.ALLOWED
+     *
+     * @param message New kick message
+     */
+    public void kickMessage(net.kyori.adventure.text.@NotNull Component message) {
+        this.message = message;
+    }
+    // Paper end
+
     /**
      * Gets the current result of the login, as an enum
      *
@@ -0,0 +0,0 @@ public class PlayerLoginEvent extends PlayerEvent {
      * Result.ALLOWED
      *
      * @return Current kick message
+     * @deprecated in favour of {@link #kickMessage()}
      */
     @NotNull
+    @Deprecated // Paper
     public String getKickMessage() {
-        return message;
+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.message); // Paper
     }
 
     /**
      * Sets the kick message to display if getResult() != Result.ALLOWED
      *
      * @param message New kick message
+     * @deprecated in favour of {@link #kickMessage(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setKickMessage(@NotNull final String message) {
-        this.message = message;
+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
     }
 
     /**
@@ -0,0 +0,0 @@ public class PlayerLoginEvent extends PlayerEvent {
      */
     public void allow() {
         result = Result.ALLOWED;
-        message = "";
+        message = net.kyori.adventure.text.Component.empty(); // Paper
     }
 
     /**
@@ -0,0 +0,0 @@ public class PlayerLoginEvent extends PlayerEvent {
      *
      * @param result New result for disallowing the player
      * @param message Kick message to display to the user
+     * @deprecated in favour of {@link #disallow(Result, net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper start
     public void disallow(@NotNull final Result result, @NotNull final String message) {
+        this.result = result;
+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message);
+    }
+    /**
+     * Disallows the player from logging in, with the given reason
+     *
+     * @param result New result for disallowing the player
+     * @param message Kick message to display to the user
+     */
+    public void disallow(@NotNull final Result result, @NotNull final net.kyori.adventure.text.Component message) {
+        // Paper end
         this.result = result;
         this.message = message;
     }
diff --git a/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java b/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
 
 /**
  * Stores details for players attempting to log in
+ * <p>
+ * When this event is fired, the player's locale is not
+ * available. Therefore, any translatable component will be
+ * rendered with the default locale, {@link java.util.Locale#US}.
+ * <p>
+ * Consider rendering any translatable yourself with {@link net.kyori.adventure.translation.GlobalTranslator#render}
+ * if the client's language is known.
  *
  * @deprecated This event causes synchronization from the login thread; {@link
  *     AsyncPlayerPreLoginEvent} is preferred to keep the secondary threads
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
 public class PlayerPreLoginEvent extends Event {
     private static final HandlerList handlers = new HandlerList();
     private Result result;
-    private String message;
+    private net.kyori.adventure.text.Component message; // Paper
     private final String name;
     private final InetAddress ipAddress;
     private final UUID uniqueId;
@@ -0,0 +0,0 @@ public class PlayerPreLoginEvent extends Event {
 
     public PlayerPreLoginEvent(@NotNull final String name, @NotNull final InetAddress ipAddress, @NotNull final UUID uniqueId) {
         this.result = Result.ALLOWED;
-        this.message = "";
+        this.message = net.kyori.adventure.text.Component.empty(); // Paper
         this.name = name;
         this.ipAddress = ipAddress;
         this.uniqueId = uniqueId;
@@ -0,0 +0,0 @@ public class PlayerPreLoginEvent extends Event {
         this.result = result;
     }
 
+    // Paper start
     /**
      * Gets the current kick message that will be used if getResult() !=
      * Result.ALLOWED
@@ -0,0 +0,0 @@ public class PlayerPreLoginEvent extends Event {
      * @return Current kick message
      */
     @NotNull
-    public String getKickMessage() {
+    public net.kyori.adventure.text.Component kickMessage() {
         return message;
     }
 
@@ -0,0 +0,0 @@ public class PlayerPreLoginEvent extends Event {
      *
      * @param message New kick message
      */
-    public void setKickMessage(@NotNull final String message) {
+    public void kickMessage(@NotNull final net.kyori.adventure.text.Component message) {
         this.message = message;
     }
 
+    /**
+     * Disallows the player from logging in, with the given reason
+     *
+     * @param result New result for disallowing the player
+     * @param message Kick message to display to the user
+     */
+    public void disallow(@NotNull final Result result, @NotNull final net.kyori.adventure.text.Component message) {
+        this.result = result;
+        this.message = message;
+    }
+    // Paper end
+    /**
+     * Gets the current kick message that will be used if getResult() !=
+     * Result.ALLOWED
+     *
+     * @return Current kick message
+     * @deprecated in favour of {@link #kickMessage()}
+     */
+    @Deprecated // Paper
+    @NotNull
+    public String getKickMessage() {
+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.message); // Paper
+    }
+
+    /**
+     * Sets the kick message to display if getResult() != Result.ALLOWED
+     *
+     * @param message New kick message
+     * @deprecated in favour of {@link #kickMessage(net.kyori.adventure.text.Component)}
+     */
+    @Deprecated // Paper
+    public void setKickMessage(@NotNull final String message) {
+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
+    }
+
     /**
      * Allows the player to log in
      */
     public void allow() {
         result = Result.ALLOWED;
-        message = "";
+        message = net.kyori.adventure.text.Component.empty(); // Paper
     }
 
     /**
@@ -0,0 +0,0 @@ public class PlayerPreLoginEvent extends Event {
      *
      * @param result New result for disallowing the player
      * @param message Kick message to display to the user
+     * @deprecated in favour of {@link #disallow(org.bukkit.event.player.PlayerPreLoginEvent.Result, net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void disallow(@NotNull final Result result, @NotNull final String message) {
         this.result = result;
-        this.message = message;
+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
     }
 
     /**
diff --git a/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java b/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  */
 public class PlayerQuitEvent extends PlayerEvent {
     private static final HandlerList handlers = new HandlerList();
-    private String quitMessage;
+    private net.kyori.adventure.text.Component quitMessage; // Paper
 
+    @Deprecated // Paper
     public PlayerQuitEvent(@NotNull final Player who, @Nullable final String quitMessage) {
         super(who);
+        this.quitMessage = quitMessage != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(quitMessage) : null; // Paper
+    }
+    // Paper start
+    public PlayerQuitEvent(@NotNull final Player who, @Nullable final net.kyori.adventure.text.Component quitMessage) {
+        super(who);
+        this.quitMessage = quitMessage;
+    }
+
+    /**
+     * Gets the quit message to send to all online players
+     *
+     * @return string quit message
+     */
+    public net.kyori.adventure.text.@Nullable Component quitMessage() {
+        return quitMessage;
+    }
+
+    /**
+     * Sets the quit message to send to all online players
+     *
+     * @param quitMessage quit message
+     */
+    public void quitMessage(net.kyori.adventure.text.@Nullable Component quitMessage) {
         this.quitMessage = quitMessage;
     }
+    // Paper end
 
     /**
      * Gets the quit message to send to all online players
      *
      * @return string quit message
+     * @deprecated in favour of {@link #quitMessage()}
      */
     @Nullable
+    @Deprecated // Paper
     public String getQuitMessage() {
-        return quitMessage;
+        return this.quitMessage == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.quitMessage); // Paper
     }
 
     /**
      * Sets the quit message to send to all online players
      *
      * @param quitMessage quit message
+     * @deprecated in favour of {@link #quitMessage(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setQuitMessage(@Nullable String quitMessage) {
-        this.quitMessage = quitMessage;
+        this.quitMessage = quitMessage != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(quitMessage) : null; // Paper
     }
 
     @NotNull
diff --git a/src/main/java/org/bukkit/event/server/BroadcastMessageEvent.java b/src/main/java/org/bukkit/event/server/BroadcastMessageEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/server/BroadcastMessageEvent.java
+++ b/src/main/java/org/bukkit/event/server/BroadcastMessageEvent.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
 
 /**
  * Event triggered for server broadcast messages such as from
- * {@link org.bukkit.Server#broadcast(String, String)}.
+ * {@link org.bukkit.Server#broadcast(net.kyori.adventure.text.Component)} (String, String)}.
  *
- * <b>This event behaves similarly to {@link AsyncPlayerChatEvent} in that it
+ * <b>This event behaves similarly to {@link io.papermc.paper.event.player.AsyncChatEvent} in that it
  * should be async if fired from an async thread. Please see that event for
  * further information.</b>
  */
 public class BroadcastMessageEvent extends ServerEvent implements Cancellable {
 
     private static final HandlerList handlers = new HandlerList();
-    private String message;
+    private net.kyori.adventure.text.Component message; // Paper
     private final Set<CommandSender> recipients;
     private boolean cancelled = false;
 
@@ -0,0 +0,0 @@ public class BroadcastMessageEvent extends ServerEvent implements Cancellable {
         this(false, message, recipients);
     }
 
+    @Deprecated // Paper
     public BroadcastMessageEvent(boolean isAsync, @NotNull String message, @NotNull Set<CommandSender> recipients) {
+        // Paper start
+        super(isAsync);
+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message);
+        this.recipients = recipients;
+    }
+
+    @Deprecated
+    public BroadcastMessageEvent(net.kyori.adventure.text.@NotNull Component message, @NotNull Set<CommandSender> recipients) {
+        this(false, message, recipients);
+    }
+
+    public BroadcastMessageEvent(boolean isAsync, net.kyori.adventure.text.@NotNull Component message, @NotNull Set<CommandSender> recipients) {
+        // Paper end
         super(isAsync);
         this.message = message;
         this.recipients = recipients;
     }
+    // Paper start
+    /**
+     * Get the broadcast message.
+     *
+     * @return Message to broadcast
+     */
+    public net.kyori.adventure.text.@NotNull Component message() {
+        return this.message;
+    }
+
+    /**
+     * Set the broadcast message.
+     *
+     * @param message New message to broadcast
+     */
+    public void message(net.kyori.adventure.text.@NotNull Component message) {
+        this.message = message;
+    }
+    // Paper end
 
     /**
      * Get the message to broadcast.
      *
      * @return Message to broadcast
+     * @deprecated in favour of {@link #message()}
      */
     @NotNull
+    @Deprecated // Paper
     public String getMessage() {
-        return message;
+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.message); // Paper
     }
 
     /**
      * Set the message to broadcast.
      *
      * @param message New message to broadcast
+     * @deprecated in favour of {@link #message(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setMessage(@NotNull String message) {
-        this.message = message;
+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
     }
 
     /**
diff --git a/src/main/java/org/bukkit/event/server/ServerListPingEvent.java b/src/main/java/org/bukkit/event/server/ServerListPingEvent.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/event/server/ServerListPingEvent.java
+++ b/src/main/java/org/bukkit/event/server/ServerListPingEvent.java
@@ -0,0 +0,0 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
     private static final HandlerList handlers = new HandlerList();
     private final String hostname;
     private final InetAddress address;
-    private String motd;
+    private net.kyori.adventure.text.Component motd; // Paper
     private final int numPlayers;
     private int maxPlayers;
 
@@ -0,0 +0,0 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
         Preconditions.checkArgument(numPlayers >= 0, "Cannot have negative number of players online", numPlayers);
         this.hostname = hostname;
         this.address = address;
-        this.motd = motd;
+        this.motd = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(motd); // Paper
         this.numPlayers = numPlayers;
         this.maxPlayers = maxPlayers;
     }
@@ -0,0 +0,0 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
      * @param address the address of the pinger
      * @param motd the message of the day
      * @param maxPlayers the max number of players
+     * @deprecated in favour of {@link #ServerListPingEvent(String, java.net.InetAddress, net.kyori.adventure.text.Component, int)}
      */
+    @Deprecated // Paper
     protected ServerListPingEvent(@NotNull final String hostname, @NotNull final InetAddress address, @NotNull final String motd, final int maxPlayers) {
         super(true);
         this.numPlayers = MAGIC_PLAYER_COUNT;
         this.hostname = hostname;
         this.address = address;
+        this.motd = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(motd); // Paper
+        this.maxPlayers = maxPlayers;
+    }
+    // Paper start
+    @Deprecated
+    public ServerListPingEvent(@NotNull final InetAddress address, @NotNull final net.kyori.adventure.text.Component motd, final int numPlayers, final int maxPlayers) {
+        this("", address, motd, numPlayers, maxPlayers);
+    }
+    public ServerListPingEvent(@NotNull final String hostname, @NotNull final InetAddress address, @NotNull final net.kyori.adventure.text.Component motd, final int numPlayers, final int maxPlayers) {
+        super(true);
+        Preconditions.checkArgument(numPlayers >= 0, "Cannot have negative number of players online (%s)", numPlayers);
+        this.hostname = hostname;
+        this.address = address;
         this.motd = motd;
+        this.numPlayers = numPlayers;
         this.maxPlayers = maxPlayers;
     }
+    /**
+     * This constructor is intended for implementations that provide the
+     * {@link #iterator()} method, thus provided the {@link #getNumPlayers()}
+     * count.
+     *
+     * @param address the address of the pinger
+     * @param motd the message of the day
+     * @param maxPlayers the max number of players
+     * @deprecated in favour of {@link #ServerListPingEvent(String, java.net.InetAddress, net.kyori.adventure.text.Component, int)}
+     */
+    @Deprecated
+    protected ServerListPingEvent(@NotNull final InetAddress address, @NotNull final net.kyori.adventure.text.Component motd, final int maxPlayers) {
+        this("", address, motd, maxPlayers);
+    }
+
+    /**
+     * This constructor is intended for implementations that provide the
+     * {@link #iterator()} method, thus provided the {@link #getNumPlayers()}
+     * count.
+     *
+     * @param hostname The hostname that was used to connect to the server
+     * @param address the address of the pinger
+     * @param motd the message of the day
+     * @param maxPlayers the max number of players
+     */
+    protected ServerListPingEvent(final @NotNull String hostname, final @NotNull InetAddress address, final net.kyori.adventure.text.@NotNull Component motd, final int maxPlayers) {
+        this.numPlayers = MAGIC_PLAYER_COUNT;
+        this.hostname = hostname;
+        this.address = address;
+        this.motd = motd;
+        this.maxPlayers = maxPlayers;
+    }
+    /**
+     * Get the message of the day message.
+     *
+     * @return the message of the day
+     */
+    public net.kyori.adventure.text.@NotNull Component motd() {
+        return motd;
+    }
+    /**
+     * Change the message of the day message.
+     *
+     * @param motd the message of the day
+     */
+    public void motd(net.kyori.adventure.text.@NotNull Component motd) {
+        this.motd = motd;
+    }
+    // Paper end
 
     /**
      * Gets the hostname that the player used to connect to the server, or
@@ -0,0 +0,0 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
      * Get the message of the day message.
      *
      * @return the message of the day
+     * @deprecated in favour of {@link #motd()}
      */
     @NotNull
+    @Deprecated // Paper
     public String getMotd() {
-        return motd;
+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.motd); // Paper
     }
 
     /**
      * Change the message of the day message.
      *
      * @param motd the message of the day
+     * @deprecated in favour of {@link #motd(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setMotd(@NotNull String motd) {
-        this.motd = motd;
+        this.motd = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(motd); // Paper
     }
 
     /**
diff --git a/src/main/java/org/bukkit/inventory/InventoryView.java b/src/main/java/org/bukkit/inventory/InventoryView.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/inventory/InventoryView.java
+++ b/src/main/java/org/bukkit/inventory/InventoryView.java
@@ -0,0 +0,0 @@ public interface InventoryView {
      */
     public boolean setProperty(@NotNull Property prop, int value);
 
+    // Paper start
     /**
      * Get the title of this inventory window.
      *
      * @return The title.
      */
     @NotNull
+    default net.kyori.adventure.text.Component title() {
+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(this.getTitle());
+    }
+    // Paper end
+
+    /**
+     * Get the title of this inventory window.
+     *
+     * @return The title.
+     * @deprecated in favour of {@link #title()}
+     */
+    @Deprecated // Paper
+    @NotNull
     public String getTitle();
 
     /**
diff --git a/src/main/java/org/bukkit/inventory/ItemFactory.java b/src/main/java/org/bukkit/inventory/ItemFactory.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/inventory/ItemFactory.java
+++ b/src/main/java/org/bukkit/inventory/ItemFactory.java
@@ -0,0 +0,0 @@ public interface ItemFactory {
      */
     @NotNull
     ItemStack enchantItem(@NotNull final ItemStack item, final int level, final boolean allowTreasures);
+
+    // Paper start - Adventure
+    /**
+     * Creates a hover event for the given item.
+     *
+     * @param item The item
+     * @return A hover event
+     */
+    @NotNull
+    net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowItem> asHoverEvent(final @NotNull ItemStack item, final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowItem> op);
+
+    /**
+     * Get the formatted display name of the {@link ItemStack}.
+     *
+     * @param itemStack the {@link ItemStack}
+     * @return display name of the {@link ItemStack}
+     */
+    @NotNull
+    net.kyori.adventure.text.Component displayName(@NotNull ItemStack itemStack);
+    // Paper end - Adventure
 }
diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/inventory/ItemStack.java
+++ b/src/main/java/org/bukkit/inventory/ItemStack.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  * use this class to encapsulate Materials for which {@link Material#isItem()}
  * returns false.</b>
  */
-public class ItemStack implements Cloneable, ConfigurationSerializable, Translatable {
+public class ItemStack implements Cloneable, ConfigurationSerializable, Translatable, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowItem> { // Paper
     private Material type = Material.AIR;
     private int amount = 0;
     private MaterialData data = null;
@@ -0,0 +0,0 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
     public String getTranslationKey() {
         return Bukkit.getUnsafe().getTranslationKey(this);
     }
+
+    // Paper start
+    @NotNull
+    @Override
+    public net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowItem> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowItem> op) {
+        return org.bukkit.Bukkit.getServer().getItemFactory().asHoverEvent(this, op);
+    }
+
+    /**
+     * Get the formatted display name of the {@link ItemStack}.
+     *
+     * @return display name of the {@link ItemStack}
+     */
+    public net.kyori.adventure.text.@NotNull Component displayName() {
+        return Bukkit.getServer().getItemFactory().displayName(this);
+    }
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/inventory/MenuType.java b/src/main/java/org/bukkit/inventory/MenuType.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/inventory/MenuType.java
+++ b/src/main/java/org/bukkit/inventory/MenuType.java
@@ -0,0 +0,0 @@ public interface MenuType extends Keyed {
          * @param player the player the view belongs to
          * @param title the title of the view
          * @return the created {@link InventoryView}
+         * @deprecated Use {@link #create(HumanEntity, net.kyori.adventure.text.Component)} instead.
          */
         @NotNull
+        @Deprecated(since = "1.21") // Paper - adventure
         V create(@NotNull HumanEntity player, @NotNull String title);
+
+        // Paper start - adventure
+        /**
+         * Creates a view of the specified menu type.
+         * <p>
+         * The player provided to create this view must be the player the view
+         * is opened for. See {@link HumanEntity#openInventory(InventoryView)}
+         * for more information.
+         *
+         * @param player the player the view belongs to
+         * @param title the title of the view
+         * @return the created {@link InventoryView}
+         */
+        @NotNull
+        V create(@NotNull HumanEntity player, @NotNull net.kyori.adventure.text.Component title);
+        // Paper end - adventure
     }
 
+    // Paper start - adventure
+    /**
+     * Creates a view of the specified menu type.
+     * <p>
+     * The player provided to create this view must be the player the view
+     * is opened for. See {@link HumanEntity#openInventory(InventoryView)}
+     * for more information.
+     *
+     * @param player the player the view belongs to
+     * @param title the title of the view
+     * @return the created {@link InventoryView}
+     */
+    @NotNull
+    InventoryView create(@NotNull HumanEntity player, @NotNull net.kyori.adventure.text.Component title);
+    // Paper end - adventure
+
     /**
      * Yields this MenuType as a typed version of itself with a plain
      * {@link InventoryView} representing it.
diff --git a/src/main/java/org/bukkit/inventory/meta/BookMeta.java b/src/main/java/org/bukkit/inventory/meta/BookMeta.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/inventory/meta/BookMeta.java
+++ b/src/main/java/org/bukkit/inventory/meta/BookMeta.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 /**
- * Represents a {@link Material#WRITTEN_BOOK}) that can have a title, an author,
+ * Represents a {@link Material#WRITTEN_BOOK} that can have a title, an author,
  * and pages.
+ * <p>
+ * Before using this type, make sure to check the itemstack's material with
+ * {@link org.bukkit.inventory.ItemStack#getType()}. {@code instanceof} on
+ * the meta instance is not sufficient due to unusual inheritance
+ * with relation to {@link WritableBookMeta}.
  */
-public interface BookMeta extends WritableBookMeta {
+public interface BookMeta extends WritableBookMeta, net.kyori.adventure.inventory.Book { // Paper - adventure
 
     /**
      * Represents the generation (or level of copying) of a written book
@@ -0,0 +0,0 @@ public interface BookMeta extends WritableBookMeta {
     @NotNull
     BookMeta clone();
 
+    // Paper start - adventure
+    //<editor-fold desc="deprecations" defaultstate="collapsed">
+    /**
+     * @deprecated use {@link #page(int)}
+     */
+    @Deprecated
+    @Override
+    @NotNull String getPage(int page);
+
+    /**
+     * @deprecated use {@link #page(int, net.kyori.adventure.text.Component)}
+     */
+    @Deprecated
+    @Override
+    void setPage(int page, @NotNull String data);
+
+    /**
+     * @deprecated use {@link #pages()}
+     */
+    @Deprecated
+    @Override
+    @NotNull List<String> getPages();
+
+    /**
+     * @deprecated use {@link #pages(List)}
+     */
+    @Deprecated
+    @Override
+    void setPages(@NotNull List<String> pages);
+
+    /**
+     * @deprecated use {@link #pages(net.kyori.adventure.text.Component...)}
+     */
+    @Deprecated
+    @Override
+    void setPages(@NotNull String... pages);
+
+    /**
+     * @deprecated use {@link #addPages(net.kyori.adventure.text.Component...)}
+     */
+    @Deprecated
+    @Override
+    void addPage(@NotNull String... pages);
+    //</editor-fold>
+
+    /**
+     * Gets the title of the book.
+     * <p>
+     * Plugins should check that hasTitle() returns true before calling this
+     * method.
+     *
+     * @return the title of the book
+     */
+    @Override
+    net.kyori.adventure.text.@Nullable Component title();
+
+    /**
+     * Sets the title of the book.
+     * <p>
+     * Limited to 32 characters. Removes title when given null.
+     *
+     * @param title the title to set
+     * @return the same {@link BookMeta} instance
+     */
+    @org.jetbrains.annotations.Contract(value = "_ -> this", pure = false)
+    @Override
+    @NotNull BookMeta title(net.kyori.adventure.text.@Nullable Component title);
+
+    /**
+     * Gets the author of the book.
+     * <p>
+     * Plugins should check that hasAuthor() returns true before calling this
+     * method.
+     *
+     * @return the author of the book
+     */
+    @Override
+    net.kyori.adventure.text.@Nullable Component author();
+
+    /**
+     * Sets the author of the book. Removes author when given null.
+     *
+     * @param author the author to set
+     * @return the same {@link BookMeta} instance
+     */
+    @org.jetbrains.annotations.Contract(value = "_ -> this", pure = false)
+    @Override
+    @NotNull BookMeta author(net.kyori.adventure.text.@Nullable Component author);
+
+
+    /**
+     * Gets the specified page in the book. The page must exist.
+     * <p>
+     * Pages are 1-indexed.
+     *
+     * @param page the page number to get, in range [1, getPageCount()]
+     * @return the page from the book
+     */
+    net.kyori.adventure.text.@NotNull Component page(int page);
+
+    /**
+     * Sets the specified page in the book. Pages of the book must be
+     * contiguous.
+     * <p>
+     * The data can be up to 1024 characters in length, additional characters
+     * are truncated.
+     * <p>
+     * Pages are 1-indexed.
+     *
+     * @param page the page number to set, in range [1, getPageCount()]
+     * @param data the data to set for that page
+     */
+    void page(int page, net.kyori.adventure.text.@NotNull Component data);
+
+    /**
+     * Adds new pages to the end of the book. Up to a maximum of 100 pages with
+     * 1024 characters per page.
+     *
+     * @param pages A list of strings, each being a page
+     */
+    void addPages(net.kyori.adventure.text.@NotNull Component @NotNull ... pages);
+
+    interface BookMetaBuilder extends net.kyori.adventure.inventory.Book.Builder {
+
+        @Override
+        @NotNull BookMetaBuilder title(net.kyori.adventure.text.@Nullable Component title);
+
+        @Override
+        @NotNull BookMetaBuilder author(net.kyori.adventure.text.@Nullable Component author);
+
+        @Override
+        @NotNull BookMetaBuilder addPage(net.kyori.adventure.text.@NotNull Component page);
+
+        @Override
+        @NotNull BookMetaBuilder pages(net.kyori.adventure.text.@NotNull Component @NotNull ... pages);
+
+        @Override
+        @NotNull BookMetaBuilder pages(java.util.@NotNull Collection<net.kyori.adventure.text.Component> pages);
+
+        @Override
+        @NotNull BookMeta build();
+    }
+
+    @Override
+    @NotNull BookMetaBuilder toBuilder();
+    // Paper end
+
     // Spigot start
     public class Spigot {
 
@@ -0,0 +0,0 @@ public interface BookMeta extends WritableBookMeta {
          *
          * @param page the page number to get
          * @return the page from the book
+         * @deprecated in favour of {@link #page(int)}
          */
         @NotNull
+        @Deprecated // Paper
         public BaseComponent[] getPage(int page) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface BookMeta extends WritableBookMeta {
          *
          * @param page the page number to set
          * @param data the data to set for that page
+         * @deprecated in favour of {@link #page(int, net.kyori.adventure.text.Component)}
          */
+        @Deprecated // Paper
         public void setPage(int page, @Nullable BaseComponent... data) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface BookMeta extends WritableBookMeta {
          * Gets all the pages in the book.
          *
          * @return list of all the pages in the book
+         * @deprecated in favour of {@link #pages()}
          */
         @NotNull
+        @Deprecated // Paper
         public List<BaseComponent[]> getPages() {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface BookMeta extends WritableBookMeta {
          * pages. Maximum 50 pages with 256 characters per page.
          *
          * @param pages A list of pages to set the book to use
+         * @deprecated in favour of {@link #pages(java.util.List)}
          */
+        @Deprecated // Paper
         public void setPages(@NotNull List<BaseComponent[]> pages) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface BookMeta extends WritableBookMeta {
          * pages. Maximum 50 pages with 256 characters per page.
          *
          * @param pages A list of component arrays, each being a page
+         * @deprecated in favour of {@link #pages(net.kyori.adventure.text.Component...)}
          */
+        @Deprecated // Paper
         public void setPages(@NotNull BaseComponent[]... pages) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
@@ -0,0 +0,0 @@ public interface BookMeta extends WritableBookMeta {
          * with 256 characters per page.
          *
          * @param pages A list of component arrays, each being a page
+         * @deprecated in favour of {@link #addPages(net.kyori.adventure.text.Component...)}
          */
+        @Deprecated // Paper
         public void addPage(@NotNull BaseComponent[]... pages) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
diff --git a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
+++ b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
@@ -0,0 +0,0 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
      */
     boolean hasDisplayName();
 
+    // Paper start
+    /**
+     * Gets the display name.
+     *
+     * <p>Plugins should check that {@link #hasDisplayName()} returns <code>true</code> before calling this method.</p>
+     *
+     * @return the display name
+     */
+    net.kyori.adventure.text.@Nullable Component displayName();
+
+    /**
+     * Sets the display name.
+     *
+     * @param displayName the display name to set
+     */
+    void displayName(final net.kyori.adventure.text.@Nullable Component displayName);
+    // Paper end
+
     /**
      * Gets the display name that is set.
      * <p>
@@ -0,0 +0,0 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
      * before calling this method.
      *
      * @return the display name that is set
+     * @deprecated in favour of {@link #displayName()}
      */
+    @Deprecated // Paper
     @NotNull
     String getDisplayName();
 
@@ -0,0 +0,0 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
      * Sets the display name.
      *
      * @param name the name to set
+     * @deprecated in favour of {@link #displayName(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     void setDisplayName(@Nullable String name);
 
     /**
@@ -0,0 +0,0 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
      */
     boolean hasItemName();
 
+    // Paper start
+    /**
+     * Gets the item name component that is set.
+     * <br>
+     * Item name differs from display name in that it is cannot be edited by an
+     * anvil, is not styled with italics, and does not show labels.
+     * <p>
+     * Plugins should check that {@link #hasItemName()} returns <code>true</code> before
+     * calling this method.
+     *
+     * @return the item name that is set
+     * @see #hasItemName()
+     */
+    @org.jetbrains.annotations.NotNull
+    Component itemName();
+
+    /**
+     * Sets the item name.
+     * <br>
+     * Item name differs from display name in that it is cannot be edited by an
+     * anvil, is not styled with italics, and does not show labels.
+     *
+     * @param name the name to set, null to remove it
+     */
+    void itemName(@Nullable final Component name);
+    // Paper end
     /**
      * Gets the item name that is set.
      * <br>
@@ -0,0 +0,0 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
      * calling this method.
      *
      * @return the item name that is set
+     * @deprecated in favour of {@link #itemName()}
      */
+    @Deprecated // Paper
     @NotNull
     String getItemName();
 
@@ -0,0 +0,0 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
      * anvil, is not styled with italics, and does not show labels.
      *
      * @param name the name to set
+     * @deprecated in favour of {@link #itemName(Component)}
      */
+    @Deprecated // Paper
     void setItemName(@Nullable String name);
 
     /**
@@ -0,0 +0,0 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
      */
     boolean hasLore();
 
+    // Paper start
+    /**
+     * Gets the lore.
+     *
+     * <p>Plugins should check that {@link #hasLore()} returns <code>true</code> before calling this method.</p>
+     *
+     * @return the lore
+     */
+    @Nullable List<net.kyori.adventure.text.Component> lore();
+
+    /**
+     * Sets the lore.
+     *
+     * @param lore the lore to set
+     */
+    void lore(final @Nullable List<? extends net.kyori.adventure.text.Component> lore);
+    // Paper end
+
     /**
      * Gets the lore that is set.
      * <p>
@@ -0,0 +0,0 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
      * calling this method.
      *
      * @return a list of lore that is set
+     * @deprecated in favour of {@link #lore()}
      */
+    @Deprecated // Paper
     @Nullable
     List<String> getLore();
 
@@ -0,0 +0,0 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
      * Removes lore when given null.
      *
      * @param lore the lore that will be set
+     * @deprecated in favour of {@link #lore(List)}
      */
+    @Deprecated // Paper
     void setLore(@Nullable List<String> lore);
 
     /**
diff --git a/src/main/java/org/bukkit/inventory/meta/WritableBookMeta.java b/src/main/java/org/bukkit/inventory/meta/WritableBookMeta.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/inventory/meta/WritableBookMeta.java
+++ b/src/main/java/org/bukkit/inventory/meta/WritableBookMeta.java
@@ -0,0 +0,0 @@ import org.bukkit.Material;
 import org.jetbrains.annotations.NotNull;
 
 /**
- * Represents a book ({@link Material#WRITABLE_BOOK} or {@link
- * Material#WRITTEN_BOOK}) that can have pages.
+ * Represents a book ({@link Material#WRITABLE_BOOK}) that can have pages.
+ * <p>
+ * For {@link Material#WRITTEN_BOOK}, use {@link BookMeta}.
+ * <p>
+ * Before using this type, make sure to check the itemstack's material with
+ * {@link org.bukkit.inventory.ItemStack#getType()}. {@code instanceof} on
+ * the meta instance is not sufficient due to unusual inheritance
+ * with relation to {@link BookMeta}.
  */
 public interface WritableBookMeta extends ItemMeta {
 
diff --git a/src/main/java/org/bukkit/inventory/meta/trim/TrimMaterial.java b/src/main/java/org/bukkit/inventory/meta/trim/TrimMaterial.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/inventory/meta/trim/TrimMaterial.java
+++ b/src/main/java/org/bukkit/inventory/meta/trim/TrimMaterial.java
@@ -0,0 +0,0 @@ public interface TrimMaterial extends Keyed, Translatable {
     private static TrimMaterial getTrimMaterial(@NotNull String key) {
         return Registry.TRIM_MATERIAL.getOrThrow(NamespacedKey.minecraft(key));
     }
+
+    // Paper start - adventure
+    /**
+     * Get the description of this {@link TrimMaterial}.
+     *
+     * @return the description
+     */
+    net.kyori.adventure.text.@org.jetbrains.annotations.NotNull Component description();
+
+    /**
+     * @deprecated this method assumes that {@link #description()} will
+     * always be a translatable component which is not guaranteed.
+     */
+    @Override
+    @Deprecated(forRemoval = true)
+    @org.jetbrains.annotations.NotNull String getTranslationKey();
+    // Paper end - adventure
 }
diff --git a/src/main/java/org/bukkit/inventory/meta/trim/TrimPattern.java b/src/main/java/org/bukkit/inventory/meta/trim/TrimPattern.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/inventory/meta/trim/TrimPattern.java
+++ b/src/main/java/org/bukkit/inventory/meta/trim/TrimPattern.java
@@ -0,0 +0,0 @@ public interface TrimPattern extends Keyed, Translatable {
     private static TrimPattern getTrimPattern(@NotNull String key) {
         return Registry.TRIM_PATTERN.getOrThrow(NamespacedKey.minecraft(key));
     }
+
+    // Paper start - adventure
+    /**
+     * Get the description of this {@link TrimPattern}.
+     *
+     * @return the description
+     */
+    net.kyori.adventure.text.@org.jetbrains.annotations.NotNull Component description();
+
+    /**
+     * @deprecated this method assumes that {@link #description()} will
+     * always be a translatable component which is not guaranteed.
+     */
+    @Override
+    @Deprecated(forRemoval = true)
+    @org.jetbrains.annotations.NotNull String getTranslationKey();
+    // Paper end - adventure
 }
diff --git a/src/main/java/org/bukkit/map/MapCursor.java b/src/main/java/org/bukkit/map/MapCursor.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/map/MapCursor.java
+++ b/src/main/java/org/bukkit/map/MapCursor.java
@@ -0,0 +0,0 @@ public final class MapCursor {
     private byte x, y;
     private byte direction;
     private boolean visible;
-    private String caption;
+    private net.kyori.adventure.text.Component caption; // Paper
     private Type type;
 
     /**
@@ -0,0 +0,0 @@ public final class MapCursor {
      */
     @Deprecated
     public MapCursor(byte x, byte y, byte direction, byte type, boolean visible) {
-        this(x, y, direction, type, visible, null);
+        this(x, y, direction, type, visible, (String) null); // Paper
     }
 
     /**
@@ -0,0 +0,0 @@ public final class MapCursor {
      * @param visible Whether the cursor is visible by default.
      */
     public MapCursor(byte x, byte y, byte direction, @NotNull Type type, boolean visible) {
-        this(x, y, direction, type, visible, null);
+        this(x, y, direction, type, visible, (String) null); // Paper
     }
 
     /**
@@ -0,0 +0,0 @@ public final class MapCursor {
      * @param type The type (color/style) of the map cursor.
      * @param visible Whether the cursor is visible by default.
      * @param caption cursor caption
-     * @deprecated Magic value, use {@link #MapCursor(byte, byte, byte, Type, boolean, String)}
+     * @deprecated Magic value. Use {@link #MapCursor(byte, byte, byte, Type, boolean, net.kyori.adventure.text.Component)}
      */
     @Deprecated
     public MapCursor(byte x, byte y, byte direction, byte type, boolean visible, @Nullable String caption) {
@@ -0,0 +0,0 @@ public final class MapCursor {
         setDirection(direction);
         setRawType(type);
         this.visible = visible;
-        this.caption = caption;
+        this.caption = caption == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(caption); // Paper
+    }
+    // Paper start
+    /**
+     * Initialize the map cursor.
+     *
+     * @param x The x coordinate, from -128 to 127.
+     * @param y The y coordinate, from -128 to 127.
+     * @param direction The facing of the cursor, from 0 to 15.
+     * @param type The type (color/style) of the map cursor.
+     * @param visible Whether the cursor is visible by default.
+     * @param caption cursor caption
+     * @deprecated Magic value
+     */
+    @Deprecated
+    public MapCursor(byte x, byte y, byte direction, byte type, boolean visible, net.kyori.adventure.text.@Nullable Component caption) {
+        this.x = x; this.y = y; this.visible = visible; this.caption = caption;
+        setDirection(direction);
+        setRawType(type);
     }
+    /**
+     * Initialize the map cursor.
+     *
+     * @param x The x coordinate, from -128 to 127.
+     * @param y The y coordinate, from -128 to 127.
+     * @param direction The facing of the cursor, from 0 to 15.
+     * @param type The type (color/style) of the map cursor.
+     * @param visible Whether the cursor is visible by default.
+     * @param caption cursor caption
+     */
+    public MapCursor(byte x, byte y, byte direction, @NotNull Type type, boolean visible, net.kyori.adventure.text.@Nullable Component caption) {
+        this.x = x; this.y = y; this.visible = visible; this.caption = caption;
+        setDirection(direction);
+        setType(type);
+    }
+    // Paper end
 
     /**
      * Initialize the map cursor.
@@ -0,0 +0,0 @@ public final class MapCursor {
         setDirection(direction);
         this.type = type;
         this.visible = visible;
-        this.caption = caption;
+        this.caption = caption == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(caption); // Paper
     }
 
     /**
@@ -0,0 +0,0 @@ public final class MapCursor {
         this.visible = visible;
     }
 
+    // Paper start
     /**
      * Gets the caption on this cursor.
      *
      * @return caption
      */
+    public net.kyori.adventure.text.@Nullable Component caption() {
+        return this.caption;
+    }
+    /**
+     * Sets the caption on this cursor.
+     *
+     * @param caption new caption
+     */
+    public void caption(net.kyori.adventure.text.@Nullable Component caption) {
+        this.caption = caption;
+    }
+    // Paper end
+    /**
+     * Gets the caption on this cursor.
+     *
+     * @return caption
+     * @deprecated in favour of {@link #caption()}
+     */
     @Nullable
+    @Deprecated // Paper
     public String getCaption() {
-        return caption;
+        return this.caption == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.caption); // Paper
     }
 
     /**
      * Sets the caption on this cursor.
      *
      * @param caption new caption
+     * @deprecated in favour of {@link #caption(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     public void setCaption(@Nullable String caption) {
-        this.caption = caption;
+        this.caption = caption == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(caption); // Paper
     }
 
     /**
diff --git a/src/main/java/org/bukkit/map/MapCursorCollection.java b/src/main/java/org/bukkit/map/MapCursorCollection.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/map/MapCursorCollection.java
+++ b/src/main/java/org/bukkit/map/MapCursorCollection.java
@@ -0,0 +0,0 @@ public final class MapCursorCollection {
     public MapCursor addCursor(int x, int y, byte direction, byte type, boolean visible, @Nullable String caption) {
         return addCursor(new MapCursor((byte) x, (byte) y, direction, type, visible, caption));
     }
+    // Paper start
+    /**
+     * Add a cursor to the collection.
+     *
+     * @param x The x coordinate, from -128 to 127.
+     * @param y The y coordinate, from -128 to 127.
+     * @param direction The facing of the cursor, from 0 to 15.
+     * @param type The type (color/style) of the map cursor.
+     * @param visible Whether the cursor is visible.
+     * @param caption banner caption
+     * @return The newly added MapCursor.
+     * @deprecated Magic value
+     */
+    @Deprecated
+    public @NotNull MapCursor addCursor(int x, int y, byte direction, byte type, boolean visible, net.kyori.adventure.text.@Nullable Component caption) {
+        return addCursor(new MapCursor((byte) x, (byte) y, direction, type, visible, caption));
+    }
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/permissions/Permissible.java b/src/main/java/org/bukkit/permissions/Permissible.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/permissions/Permissible.java
+++ b/src/main/java/org/bukkit/permissions/Permissible.java
@@ -0,0 +0,0 @@ public interface Permissible extends ServerOperator {
      */
     @NotNull
     public Set<PermissionAttachmentInfo> getEffectivePermissions();
+
+    // Paper start - add TriState permission checks
+    /**
+     * Checks if this object has a permission set and, if it is set, the value of the permission.
+     *
+     * @param permission the permission to check
+     * @return a tri-state of if the permission is set and, if it is set, it's value
+     */
+    default net.kyori.adventure.util.@NotNull TriState permissionValue(final @NotNull Permission permission) {
+        if (this.isPermissionSet(permission)) {
+            return net.kyori.adventure.util.TriState.byBoolean(this.hasPermission(permission));
+        } else {
+            return net.kyori.adventure.util.TriState.NOT_SET;
+        }
+    }
+
+    /**
+     * Checks if this object has a permission set and, if it is set, the value of the permission.
+     *
+     * @param permission the permission to check
+     * @return a tri-state of if the permission is set and, if it is set, it's value
+     */
+    default net.kyori.adventure.util.@NotNull TriState permissionValue(final @NotNull String permission) {
+        if (this.isPermissionSet(permission)) {
+            return net.kyori.adventure.util.TriState.byBoolean(this.hasPermission(permission));
+        } else {
+            return net.kyori.adventure.util.TriState.NOT_SET;
+        }
+    }
+    // Paper end
 }
diff --git a/src/main/java/org/bukkit/plugin/Plugin.java b/src/main/java/org/bukkit/plugin/Plugin.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/plugin/Plugin.java
+++ b/src/main/java/org/bukkit/plugin/Plugin.java
@@ -0,0 +0,0 @@ public interface Plugin extends TabExecutor {
     @NotNull
     public Logger getLogger();
 
+    // Paper start - Adventure component logger
+    @NotNull
+    default net.kyori.adventure.text.logger.slf4j.ComponentLogger getComponentLogger() {
+        return net.kyori.adventure.text.logger.slf4j.ComponentLogger.logger(getLogger().getName());
+    }
+    // Paper end
+
     /**
      * Returns the name of the plugin.
      * <p>
diff --git a/src/main/java/org/bukkit/scoreboard/Objective.java b/src/main/java/org/bukkit/scoreboard/Objective.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/scoreboard/Objective.java
+++ b/src/main/java/org/bukkit/scoreboard/Objective.java
@@ -0,0 +0,0 @@ public interface Objective {
     @NotNull
     String getName();
 
+    // Paper start - Adventure
+    /**
+     * Gets the display name for this objective
+     *
+     * @return this objective's display name
+     * @throws IllegalStateException if this objective has been unregistered
+     */
+    net.kyori.adventure.text.@NotNull Component displayName();
+    /**
+     * Sets the name displayed to players for this objective.
+     *
+     * @param displayName Display name to set
+     * @throws IllegalStateException if this objective has been unregistered
+     * @throws IllegalArgumentException if displayName is null
+     * @throws IllegalArgumentException if displayName is longer than 128
+     *     characters.
+     */
+    void displayName(net.kyori.adventure.text.@Nullable Component displayName);
+    // Paper end - Adventure
+
     /**
      * Gets the name displayed to players for this objective
      *
      * @return this objective's display name
      * @throws IllegalStateException if this objective has been unregistered
+     * @deprecated in favour of {@link #displayName()}
      */
     @NotNull
+    @Deprecated // Paper
     String getDisplayName();
 
     /**
@@ -0,0 +0,0 @@ public interface Objective {
      *
      * @param displayName Display name to set
      * @throws IllegalStateException if this objective has been unregistered
+     * @deprecated in favour of {@link #displayName(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     void setDisplayName(@NotNull String displayName);
 
     /**
diff --git a/src/main/java/org/bukkit/scoreboard/Scoreboard.java b/src/main/java/org/bukkit/scoreboard/Scoreboard.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/scoreboard/Scoreboard.java
+++ b/src/main/java/org/bukkit/scoreboard/Scoreboard.java
@@ -0,0 +0,0 @@ public interface Scoreboard {
     @NotNull
     Objective registerNewObjective(@NotNull String name, @NotNull String criteria);
 
+    // Paper start - Adventure
+    /**
+     * Registers an Objective on this Scoreboard
+     *
+     * @param name Name of the Objective
+     * @param criteria Criteria for the Objective
+     * @param displayName display name for the Objective.
+     * @return The registered Objective
+     * @throws IllegalArgumentException if name is longer than 32767
+     *     characters.
+     * @throws IllegalArgumentException if an objective by that name already
+     *     exists
+     * @deprecated use {@link #registerNewObjective(String, Criteria, net.kyori.adventure.text.Component)}
+     */
+    @NotNull
+    @Deprecated
+    Objective registerNewObjective(@NotNull String name, @NotNull String criteria, net.kyori.adventure.text.@Nullable Component displayName);
+    /**
+     * Registers an Objective on this Scoreboard
+     *
+     * @param name Name of the Objective
+     * @param criteria Criteria for the Objective
+     * @param displayName Name displayed to players for the Objective.
+     * @param renderType Manner of rendering the Objective
+     * @return The registered Objective
+     * @throws IllegalArgumentException if name is longer than 32767
+     *     characters.
+     * @throws IllegalArgumentException if an objective by that name already
+     *     exists
+     * @deprecated use {@link #registerNewObjective(String, Criteria, net.kyori.adventure.text.Component, RenderType)}
+     */
+    @NotNull
+    @Deprecated
+    Objective registerNewObjective(@NotNull String name, @NotNull String criteria, net.kyori.adventure.text.@Nullable Component displayName, @NotNull RenderType renderType) throws IllegalArgumentException;
+    /**
+     * Registers an Objective on this Scoreboard
+     *
+     * @param name Name of the Objective
+     * @param criteria Criteria for the Objective
+     * @param displayName Name displayed to players for the Objective.
+     * @return The registered Objective
+     * @throws IllegalArgumentException if name is longer than 32767
+     *     characters.
+     * @throws IllegalArgumentException if an objective by that name already
+     *     exists
+     */
+    @NotNull
+    Objective registerNewObjective(@NotNull String name, @NotNull Criteria criteria, net.kyori.adventure.text.@Nullable Component displayName) throws IllegalArgumentException;
+    /**
+     * Registers an Objective on this Scoreboard
+     *
+     * @param name Name of the Objective
+     * @param criteria Criteria for the Objective
+     * @param displayName Name displayed to players for the Objective.
+     * @param renderType Manner of rendering the Objective
+     * @return The registered Objective
+     * @throws IllegalArgumentException if name is longer than 32767
+     *     characters.
+     * @throws IllegalArgumentException if an objective by that name already
+     *     exists
+     */
+    @NotNull
+    Objective registerNewObjective(@NotNull String name, @NotNull Criteria criteria, net.kyori.adventure.text.@Nullable Component displayName, @NotNull RenderType renderType) throws IllegalArgumentException;
+    // Paper end - Adventure
+
     /**
      * Registers an Objective on this Scoreboard
      *
@@ -0,0 +0,0 @@ public interface Scoreboard {
      *     characters.
      * @throws IllegalArgumentException if an objective by that name already
      *     exists
-     * @deprecated use {@link #registerNewObjective(String, Criteria, String)}
+     * @deprecated use {@link #registerNewObjective(String, Criteria, net.kyori.adventure.text.Component)}
      */
     @Deprecated
     @NotNull
@@ -0,0 +0,0 @@ public interface Scoreboard {
      *     characters.
      * @throws IllegalArgumentException if an objective by that name already
      *     exists
-     * @deprecated use {@link #registerNewObjective(String, Criteria, String, RenderType)}
+     * @deprecated use {@link #registerNewObjective(String, Criteria, net.kyori.adventure.text.Component, RenderType)}
      */
     @Deprecated
     @NotNull
@@ -0,0 +0,0 @@ public interface Scoreboard {
      *     characters.
      * @throws IllegalArgumentException if an objective by that name already
      *     exists
+     * @deprecated in favour of {@link #registerNewObjective(String, Criteria, net.kyori.adventure.text.Component)}
      */
     @NotNull
+    @Deprecated // Paper
     Objective registerNewObjective(@NotNull String name, @NotNull Criteria criteria, @NotNull String displayName);
 
     /**
@@ -0,0 +0,0 @@ public interface Scoreboard {
      *     characters.
      * @throws IllegalArgumentException if an objective by that name already
      *     exists
+     * @deprecated in favour of {@link #registerNewObjective(String, Criteria, net.kyori.adventure.text.Component, RenderType)}
      */
     @NotNull
+    @Deprecated // Paper
     Objective registerNewObjective(@NotNull String name, @NotNull Criteria criteria, @NotNull String displayName, @NotNull RenderType renderType);
 
     /**
diff --git a/src/main/java/org/bukkit/scoreboard/Team.java b/src/main/java/org/bukkit/scoreboard/Team.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/scoreboard/Team.java
+++ b/src/main/java/org/bukkit/scoreboard/Team.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  * properties. This team is only relevant to the display of the associated
  * {@link #getScoreboard() scoreboard}.
  */
-public interface Team {
+public interface Team extends net.kyori.adventure.audience.ForwardingAudience { // Paper - Make Team extend ForwardingAudience
 
     /**
      * Gets the name of this Team
@@ -0,0 +0,0 @@ public interface Team {
     @NotNull
     String getName();
 
+    // Paper start - Adventure
+    /**
+     * Gets the display name for this team
+     *
+     * @return Team display name
+     * @throws IllegalStateException if this team has been unregistered
+     */
+    net.kyori.adventure.text.@NotNull Component displayName();
+
+    /**
+     * Sets the name displayed to entries for this team
+     *
+     * @param displayName New display name
+     * @throws IllegalStateException if this team has been unregistered
+     */
+    void displayName(net.kyori.adventure.text.@Nullable Component displayName);
+
+    /**
+     * Gets the prefix prepended to the display of entries on this team.
+     *
+     * @return Team prefix
+     * @throws IllegalStateException if this team has been unregistered
+     */
+    net.kyori.adventure.text.@NotNull Component prefix();
+
+    /**
+     * Sets the prefix prepended to the display of entries on this team.
+     *
+     * @param prefix New prefix
+     * @throws IllegalStateException if this team has been unregistered
+     */
+    void prefix(net.kyori.adventure.text.@Nullable Component prefix);
+
+    /**
+     * Gets the suffix appended to the display of entries on this team.
+     *
+     * @return the team's current suffix
+     * @throws IllegalStateException if this team has been unregistered
+     */
+    net.kyori.adventure.text.@NotNull Component suffix();
+
+    /**
+     * Sets the suffix appended to the display of entries on this team.
+     *
+     * @param suffix the new suffix for this team.
+     * @throws IllegalStateException if this team has been unregistered
+     */
+    void suffix(net.kyori.adventure.text.@Nullable Component suffix);
+
+    /**
+     * Checks if the team has a color specified
+     *
+     * @return true if it has a <b>color</b>
+     * @throws IllegalStateException if this team has been unregistered
+     */
+    boolean hasColor();
+
+    /**
+     * Gets the color of the team.
+     * <br>
+     * This only sets the team outline, other occurrences of colors such as in
+     * names are handled by prefixes / suffixes.
+     *
+     * @return team color
+     * @throws IllegalStateException if this team has been unregistered
+     * @throws IllegalStateException if the team doesn't have a color
+     * @see #hasColor()
+     */
+    net.kyori.adventure.text.format.@NotNull TextColor color();
+
+    /**
+     * Sets the color of the team.
+     * <br>
+     * This only sets the team outline, other occurrences of colors such as in
+     * names are handled by prefixes / suffixes.
+     *
+     * @param color new color, null for no color
+     */
+    void color(net.kyori.adventure.text.format.@Nullable NamedTextColor color);
+    // Paper end - Adventure
+
     /**
      * Gets the name displayed to entries for this team
      *
      * @return Team display name
      * @throws IllegalStateException if this team has been unregistered
+     * @deprecated in favour of {@link #displayName()}
      */
     @NotNull
+    @Deprecated // Paper
     String getDisplayName();
 
     /**
@@ -0,0 +0,0 @@ public interface Team {
      *
      * @param displayName New display name
      * @throws IllegalStateException if this team has been unregistered
+     * @deprecated in favour of {@link #displayName(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     void setDisplayName(@NotNull String displayName);
 
     /**
@@ -0,0 +0,0 @@ public interface Team {
      *
      * @return Team prefix
      * @throws IllegalStateException if this team has been unregistered
+     * @deprecated in favour of {@link #prefix()}
      */
     @NotNull
+    @Deprecated // Paper
     String getPrefix();
 
     /**
@@ -0,0 +0,0 @@ public interface Team {
      *
      * @param prefix New prefix
      * @throws IllegalStateException if this team has been unregistered
+     * @deprecated in favour of {@link #prefix(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     void setPrefix(@NotNull String prefix);
 
     /**
@@ -0,0 +0,0 @@ public interface Team {
      *
      * @return the team's current suffix
      * @throws IllegalStateException if this team has been unregistered
+     * @deprecated in favour of {@link #suffix()}
      */
     @NotNull
+    @Deprecated // Paper
     String getSuffix();
 
     /**
@@ -0,0 +0,0 @@ public interface Team {
      *
      * @param suffix the new suffix for this team.
      * @throws IllegalStateException if this team has been unregistered
+     * @deprecated in favour of {@link #suffix(net.kyori.adventure.text.Component)}
      */
+    @Deprecated // Paper
     void setSuffix(@NotNull String suffix);
 
     /**
@@ -0,0 +0,0 @@ public interface Team {
      *
      * @return team color, defaults to {@link ChatColor#RESET}
      * @throws IllegalStateException if this team has been unregistered
+     * @deprecated in favour of {@link #color()}
      */
     @NotNull
+    @Deprecated // Paper
     ChatColor getColor();
 
     /**
@@ -0,0 +0,0 @@ public interface Team {
      *
      * @param color new color, must be non-null. Use {@link ChatColor#RESET} for
      * no color
+     * @deprecated in favour of {@link #color(net.kyori.adventure.text.format.NamedTextColor)}
      */
+    @Deprecated // Paper
     void setColor(@NotNull ChatColor color);
 
     /**
diff --git a/src/test/java/io/papermc/paper/adventure/KeyTest.java b/src/test/java/io/papermc/paper/adventure/KeyTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/test/java/io/papermc/paper/adventure/KeyTest.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.adventure;
+
+import java.util.HashSet;
+import java.util.Set;
+import net.kyori.adventure.key.Key;
+import org.bukkit.NamespacedKey;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class KeyTest {
+
+    @Test
+    public void equalsTest() {
+        Key key = new NamespacedKey("test", "key");
+        Key key1 = Key.key("test", "key");
+        assertEquals(key, key1);
+        assertEquals(key1, key);
+    }
+
+    @Test
+    public void setTest() {
+        Key key = new NamespacedKey("test", "key");
+        Key key1 = Key.key("test", "key");
+        Set<Key> set = new HashSet<>(Set.of(key));
+        Set<Key> set1 = new HashSet<>(Set.of(key1));
+        assertTrue(set.contains(key1));
+        assertTrue(set1.contains(key));
+    }
+}