diff --git a/platform-bukkit/src/main/java/net/kyori/adventure/platform/bukkit/MinecraftComponentSerializer.java b/platform-bukkit/src/main/java/net/kyori/adventure/platform/bukkit/MinecraftComponentSerializer.java index f46539b8..1d39836e 100644 --- a/platform-bukkit/src/main/java/net/kyori/adventure/platform/bukkit/MinecraftComponentSerializer.java +++ b/platform-bukkit/src/main/java/net/kyori/adventure/platform/bukkit/MinecraftComponentSerializer.java @@ -93,11 +93,15 @@ public static boolean isSupported() { private static final Object MC_TEXT_GSON; private static final MethodHandle TEXT_SERIALIZER_DESERIALIZE; private static final MethodHandle TEXT_SERIALIZER_SERIALIZE; + private static final MethodHandle TEXT_SERIALIZER_DESERIALIZE_TREE; + private static final MethodHandle TEXT_SERIALIZER_SERIALIZE_TREE; static { Object gson = null; MethodHandle textSerializerDeserialize = null; MethodHandle textSerializerSerialize = null; + MethodHandle textSerializerDeserializeTree = null; + MethodHandle textSerializerSerializeTree = null; try { if (CLASS_CHAT_COMPONENT != null) { @@ -126,26 +130,45 @@ public static boolean isSupported() { if (gsonField != null) { gsonField.setAccessible(true); gson = gsonField.get(null); - } else { - final Method[] declaredMethods = chatSerializerClass.getDeclaredMethods(); - final Method deserialize = Arrays.stream(declaredMethods) - .filter(m -> Modifier.isStatic(m.getModifiers())) - .filter(m -> CLASS_CHAT_COMPONENT.isAssignableFrom(m.getReturnType())) - .filter(m -> m.getParameterCount() == 1 && m.getParameterTypes()[0].equals(String.class)) - .min(Comparator.comparing(Method::getName)) // prefer the #a method - .orElse(null); - final Method serialize = Arrays.stream(declaredMethods) - .filter(m -> Modifier.isStatic(m.getModifiers())) - .filter(m -> m.getReturnType().equals(String.class)) - .filter(m -> m.getParameterCount() == 1 && CLASS_CHAT_COMPONENT.isAssignableFrom(m.getParameterTypes()[0])) - .findFirst() - .orElse(null); - if (deserialize != null) { - textSerializerDeserialize = lookup().unreflect(deserialize); - } - if (serialize != null) { - textSerializerSerialize = lookup().unreflect(serialize); - } + } + } + for (Class serializerClass : CLASS_CHAT_COMPONENT.getClasses()) { + final Method[] declaredMethods = serializerClass.getDeclaredMethods(); + final Method deserialize = Arrays.stream(declaredMethods) + .filter(m -> Modifier.isStatic(m.getModifiers())) + .filter(m -> CLASS_CHAT_COMPONENT.isAssignableFrom(m.getReturnType())) + .filter(m -> m.getParameterCount() == 1 && m.getParameterTypes()[0].equals(String.class)) + .min(Comparator.comparing(Method::getName)) // prefer the #a method + .orElse(null); + final Method serialize = Arrays.stream(declaredMethods) + .filter(m -> Modifier.isStatic(m.getModifiers())) + .filter(m -> m.getReturnType().equals(String.class)) + .filter(m -> m.getParameterCount() == 1 && CLASS_CHAT_COMPONENT.isAssignableFrom(m.getParameterTypes()[0])) + .findFirst() + .orElse(null); + final Method deserializeTree = Arrays.stream(declaredMethods) + .filter(m -> Modifier.isStatic(m.getModifiers())) + .filter(m -> CLASS_CHAT_COMPONENT.isAssignableFrom(m.getReturnType())) + .filter(m -> m.getParameterCount() == 1 && m.getParameterTypes()[0].equals(JsonElement.class)) + .min(Comparator.comparing(Method::getName)) // prefer the #a method + .orElse(null); + final Method serializeTree = Arrays.stream(declaredMethods) + .filter(m -> Modifier.isStatic(m.getModifiers())) + .filter(m -> m.getReturnType().equals(JsonElement.class)) + .filter(m -> m.getParameterCount() == 1 && CLASS_CHAT_COMPONENT.isAssignableFrom(m.getParameterTypes()[0])) + .findFirst() + .orElse(null); + if (deserialize != null) { + textSerializerDeserialize = lookup().unreflect(deserialize); + } + if (serialize != null) { + textSerializerSerialize = lookup().unreflect(serialize); + } + if (deserializeTree != null) { + textSerializerDeserializeTree = lookup().unreflect(deserializeTree); + } + if (serializeTree != null) { + textSerializerSerializeTree = lookup().unreflect(serializeTree); } } } @@ -156,20 +179,26 @@ public static boolean isSupported() { MC_TEXT_GSON = gson; TEXT_SERIALIZER_DESERIALIZE = textSerializerDeserialize; TEXT_SERIALIZER_SERIALIZE = textSerializerSerialize; + TEXT_SERIALIZER_DESERIALIZE_TREE = textSerializerDeserializeTree; + TEXT_SERIALIZER_SERIALIZE_TREE = textSerializerSerializeTree; } - private static final boolean SUPPORTED = MC_TEXT_GSON != null || (TEXT_SERIALIZER_DESERIALIZE != null && TEXT_SERIALIZER_SERIALIZE != null); + private static final boolean SUPPORTED = MC_TEXT_GSON != null || (TEXT_SERIALIZER_DESERIALIZE != null && TEXT_SERIALIZER_SERIALIZE != null) || (TEXT_SERIALIZER_DESERIALIZE_TREE != null && TEXT_SERIALIZER_SERIALIZE_TREE != null); @Override public @NotNull Component deserialize(final @NotNull Object input) { if (!SUPPORTED) throw INITIALIZATION_ERROR.get(); try { - if (MC_TEXT_GSON != null) { - final JsonElement element = ((Gson) MC_TEXT_GSON).toJsonTree(input); - return gson().serializer().fromJson(element, Component.class); + final JsonElement element; + if (TEXT_SERIALIZER_SERIALIZE_TREE != null) { + element = (JsonElement) TEXT_SERIALIZER_SERIALIZE_TREE.invoke(input); + } else if (MC_TEXT_GSON != null) { + element = ((Gson) MC_TEXT_GSON).toJsonTree(input); + } else { + return gson().deserialize((String) TEXT_SERIALIZER_SERIALIZE.invoke(input)); } - return GsonComponentSerializer.gson().deserialize((String) TEXT_SERIALIZER_SERIALIZE.invoke(input)); + return gson().serializer().fromJson(element, Component.class); } catch (final Throwable error) { throw new UnsupportedOperationException(error); } @@ -179,9 +208,12 @@ public static boolean isSupported() { public @NotNull Object serialize(final @NotNull Component component) { if (!SUPPORTED) throw INITIALIZATION_ERROR.get(); - if (MC_TEXT_GSON != null) { + if (TEXT_SERIALIZER_DESERIALIZE_TREE != null || MC_TEXT_GSON != null) { final JsonElement json = gson().serializer().toJsonTree(component); try { + if (TEXT_SERIALIZER_DESERIALIZE_TREE != null) { + return TEXT_SERIALIZER_DESERIALIZE_TREE.invoke(json); + } return ((Gson) MC_TEXT_GSON).fromJson(json, CLASS_CHAT_COMPONENT); } catch (final Throwable error) { throw new UnsupportedOperationException(error);