diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 58e8e11bf..7557ed42c 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -12,7 +12,8 @@ spotless = "com.diffplug.spotless:6.25.0"
[libraries]
adventure-bom = "net.kyori:adventure-bom:4.17.0"
-adventure-facet = "net.kyori:adventure-platform-facet:4.3.2"
+adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.17.0"
+adventure-facet = "net.kyori:adventure-platform-facet:4.3.4"
asm = "org.ow2.asm:asm:9.6"
auto-service = "com.google.auto.service:auto-service:1.0.1"
auto-service-annotations = "com.google.auto.service:auto-service-annotations:1.0.1"
diff --git a/proxy/build.gradle.kts b/proxy/build.gradle.kts
index 035ff24de..9b74dae57 100644
--- a/proxy/build.gradle.kts
+++ b/proxy/build.gradle.kts
@@ -127,7 +127,7 @@ dependencies {
runtimeOnly(libs.disruptor)
implementation(libs.fastutil)
implementation(platform(libs.adventure.bom))
- implementation("net.kyori:adventure-nbt")
+ implementation(libs.adventure.text.serializer.json.legacy.impl)
implementation(libs.adventure.facet)
implementation(libs.completablefutures)
implementation(libs.nightconfig)
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java
index 797a58223..ca63c1e56 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java
@@ -25,7 +25,6 @@ import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.proxy.crypto.IdentifiedKeyImpl;
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
-import com.velocitypowered.proxy.protocol.util.VelocityLegacyHoverEventSerializer;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
@@ -47,6 +46,7 @@ import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.json.JSONOptions;
+import net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer;
import net.kyori.option.OptionState;
/**
@@ -58,8 +58,7 @@ public enum ProtocolUtils {
private static final GsonComponentSerializer PRE_1_16_SERIALIZER =
GsonComponentSerializer.builder()
.downsampleColors()
- .emitLegacyHoverEvent()
- .legacyHoverEventSerializer(VelocityLegacyHoverEventSerializer.INSTANCE)
+ .legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
.options(
OptionState.optionState()
// before 1.16
@@ -74,7 +73,7 @@ public enum ProtocolUtils {
.build();
private static final GsonComponentSerializer PRE_1_20_3_SERIALIZER =
GsonComponentSerializer.builder()
- .legacyHoverEventSerializer(VelocityLegacyHoverEventSerializer.INSTANCE)
+ .legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
.options(
OptionState.optionState()
// after 1.16
@@ -89,7 +88,7 @@ public enum ProtocolUtils {
.build();
private static final GsonComponentSerializer MODERN_SERIALIZER =
GsonComponentSerializer.builder()
- .legacyHoverEventSerializer(VelocityLegacyHoverEventSerializer.INSTANCE)
+ .legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
.options(
OptionState.optionState()
// after 1.16
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/VelocityLegacyHoverEventSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/VelocityLegacyHoverEventSerializer.java
deleted file mode 100644
index e667a9b16..000000000
--- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/VelocityLegacyHoverEventSerializer.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2020-2023 Velocity Contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.velocitypowered.proxy.protocol.util;
-
-import java.io.IOException;
-import java.util.UUID;
-import net.kyori.adventure.key.Key;
-import net.kyori.adventure.nbt.CompoundBinaryTag;
-import net.kyori.adventure.nbt.TagStringIO;
-import net.kyori.adventure.nbt.api.BinaryTagHolder;
-import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.event.HoverEvent;
-import net.kyori.adventure.text.event.HoverEvent.ShowEntity;
-import net.kyori.adventure.text.event.HoverEvent.ShowItem;
-import net.kyori.adventure.text.serializer.gson.LegacyHoverEventSerializer;
-import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
-import net.kyori.adventure.util.Codec.Decoder;
-import net.kyori.adventure.util.Codec.Encoder;
-import org.checkerframework.checker.nullness.qual.NonNull;
-
-/**
- * An implementation of {@link LegacyHoverEventSerializer} that implements the interface in the most
- * literal, albeit "incompatible" way possible.
- */
-public class VelocityLegacyHoverEventSerializer implements LegacyHoverEventSerializer {
-
- public static final LegacyHoverEventSerializer INSTANCE =
- new VelocityLegacyHoverEventSerializer();
-
- private VelocityLegacyHoverEventSerializer() {
-
- }
-
- private static Key legacyIdToFakeKey(byte id) {
- return Key.key("velocity", "legacy_hover/id_" + id);
- }
-
- @Override
- public HoverEvent.@NonNull ShowItem deserializeShowItem(@NonNull Component input)
- throws IOException {
- String snbt = PlainTextComponentSerializer.plainText().serialize(input);
- CompoundBinaryTag item = TagStringIO.get().asCompound(snbt);
-
- Key key;
- String idIfString = item.getString("id", "");
- if (idIfString.isEmpty()) {
- key = legacyIdToFakeKey(item.getByte("id"));
- } else {
- key = Key.key(idIfString);
- }
-
- byte count = item.getByte("Count", (byte) 1);
- return ShowItem.of(key, count, BinaryTagHolder.binaryTagHolder(snbt));
- }
-
- @Override
- public HoverEvent.@NonNull ShowEntity deserializeShowEntity(@NonNull Component input,
- Decoder componentDecoder) throws IOException {
- String snbt = PlainTextComponentSerializer.plainText().serialize(input);
- CompoundBinaryTag item = TagStringIO.get().asCompound(snbt);
-
- Component name;
- try {
- name = componentDecoder.decode(item.getString("name"));
- } catch (Exception e) {
- name = Component.text(item.getString("name"));
- }
-
- return ShowEntity.of(Key.key(item.getString("type")),
- UUID.fromString(item.getString("id")),
- name);
- }
-
- @Override
- public @NonNull Component serializeShowItem(HoverEvent.@NonNull ShowItem input)
- throws IOException {
- final CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
- .putByte("Count", (byte) input.count());
-
- String keyAsString = input.item().asString();
- if (keyAsString.startsWith("velocity:legacy_hover/id_")) {
- builder.putByte("id", Byte.parseByte(keyAsString
- .substring("velocity:legacy_hover/id_".length())));
- } else {
- builder.putString("id", keyAsString);
- }
-
- BinaryTagHolder nbt = input.nbt();
- if (nbt != null) {
- builder.put("tag", TagStringIO.get().asCompound(nbt.string()));
- }
-
- return Component.text(TagStringIO.get().asString(builder.build()));
- }
-
- @Override
- public @NonNull Component serializeShowEntity(HoverEvent.@NonNull ShowEntity input,
- Encoder componentEncoder) throws IOException {
- CompoundBinaryTag.Builder tag = CompoundBinaryTag.builder()
- .putString("id", input.id().toString())
- .putString("type", input.type().asString());
- Component name = input.name();
- if (name != null) {
- tag.putString("name", componentEncoder.encode(name));
- }
- return Component.text(TagStringIO.get().asString(tag.build()));
- }
-}