Skip to content

Commit 859f55c

Browse files
committed
Add Bukkit 1.21.6 support
1 parent 7ece7ac commit 859f55c

File tree

1 file changed

+79
-1
lines changed

1 file changed

+79
-1
lines changed

platform-bukkit/src/main/java/net/kyori/adventure/platform/bukkit/MinecraftComponentSerializer.java

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import java.lang.reflect.Field;
3030
import java.lang.reflect.Method;
3131
import java.lang.reflect.Modifier;
32+
import java.lang.reflect.ParameterizedType;
33+
import java.lang.reflect.Type;
3234
import java.util.ArrayList;
3335
import java.util.Arrays;
3436
import java.util.Comparator;
@@ -89,36 +91,57 @@ public static boolean isSupported() {
8991

9092
private static final @Nullable Class<?> CLASS_JSON_DESERIALIZER = findClass("com.goo".concat("gle.gson.JsonDeserializer")); // Hide from relocation checkers
9193
private static final @Nullable Class<?> CLASS_JSON_ELEMENT = findClass("com.goo".concat("gle.gson.JsonElement"));
94+
private static final @Nullable Class<?> CLASS_JSON_OPS = findClass("com.mo".concat("jang.serialization.JsonOps"));
9295
private static final @Nullable Class<?> CLASS_JSON_PARSER = findClass("com.goo".concat("gle.gson.JsonParser"));
9396
private static final @Nullable Class<?> CLASS_CHAT_COMPONENT = findClass(
9497
findNmsClassName("IChatBaseComponent"),
9598
findMcClassName("network.chat.IChatBaseComponent"),
9699
findMcClassName("network.chat.Component")
97100
);
101+
private static final @Nullable Class<?> CLASS_COMPONENT_SERIALIZATION = findClass(findMcClassName("network.chat.ComponentSerialization"));
98102
private static final @Nullable Class<?> CLASS_CRAFT_REGISTRY = findCraftClass("CraftRegistry");
103+
private static final @Nullable Class<?> CLASS_HOLDERLOOKUP_PROVIDER = findClass(
104+
findMcClassName("core.HolderLookup$Provider"), // Paper mapping
105+
findMcClassName("core.HolderLookup$a") // Spigot mapping
106+
);
99107
private static final @Nullable Class<?> CLASS_REGISTRY_ACCESS = findClass(
100108
findMcClassName("core.IRegistryCustom"),
101109
findMcClassName("core.RegistryAccess")
102110
);
103111
private static final @Nullable MethodHandle PARSE_JSON = findMethod(CLASS_JSON_PARSER, "parse", CLASS_JSON_ELEMENT, String.class);
104112
private static final @Nullable MethodHandle GET_REGISTRY = findStaticMethod(CLASS_CRAFT_REGISTRY, "getMinecraftRegistry", CLASS_REGISTRY_ACCESS);
105113
private static final AtomicReference<RuntimeException> INITIALIZATION_ERROR = new AtomicReference<>(new UnsupportedOperationException());
114+
private static final Object JSON_OPS_INSTANCE;
106115
private static final Object JSON_PARSER_INSTANCE;
107116
private static final Object MC_TEXT_GSON;
108117
private static final MethodHandle TEXT_SERIALIZER_DESERIALIZE;
109118
private static final MethodHandle TEXT_SERIALIZER_SERIALIZE;
110119
private static final MethodHandle TEXT_SERIALIZER_DESERIALIZE_TREE;
111120
private static final MethodHandle TEXT_SERIALIZER_SERIALIZE_TREE;
121+
private static final MethodHandle COMPONENTSERIALIZATION_CODEC_ENCODE;
122+
private static final MethodHandle COMPONENTSERIALIZATION_CODEC_DECODE;
123+
private static final Object REGISTRY_ACCESS_EMPTY;
124+
private static final MethodHandle CREATE_SERIALIZATION_CONTEXT;
112125

113126
static {
114127
Object gson = null;
128+
Object jsonOpsInstance = null;
115129
Object jsonParserInstance = null;
116130
MethodHandle textSerializerDeserialize = null;
117131
MethodHandle textSerializerSerialize = null;
118132
MethodHandle textSerializerDeserializeTree = null;
119133
MethodHandle textSerializerSerializeTree = null;
134+
MethodHandle codecEncode = null;
135+
MethodHandle codecDecode = null;
136+
Object registryAccessEmpty = null;
137+
MethodHandle createContext = null;
120138

121139
try {
140+
if (CLASS_JSON_OPS != null) {
141+
final Field instanceField = CLASS_JSON_OPS.getField("INSTANCE");
142+
instanceField.setAccessible(true);
143+
jsonOpsInstance = instanceField.get(null);
144+
}
122145
if (CLASS_JSON_PARSER != null) {
123146
jsonParserInstance = CLASS_JSON_PARSER.getDeclaredConstructor().newInstance();
124147
}
@@ -218,19 +241,60 @@ public static boolean isSupported() {
218241
}
219242
}
220243
}
244+
if (CLASS_REGISTRY_ACCESS != null) {
245+
for (Field field : CLASS_REGISTRY_ACCESS.getDeclaredFields()) {
246+
field.setAccessible(true);
247+
if (Modifier.isStatic(field.getModifiers()) && CLASS_REGISTRY_ACCESS.isAssignableFrom(field.getType())) {
248+
registryAccessEmpty = field.get(null);
249+
break;
250+
}
251+
}
252+
if (registryAccessEmpty != null && CLASS_HOLDERLOOKUP_PROVIDER != null) {
253+
for (Method m : CLASS_HOLDERLOOKUP_PROVIDER.getDeclaredMethods()) {
254+
m.setAccessible(true);
255+
if (m.getParameterCount() == 1 && m.getParameterTypes()[0].getSimpleName().equals("DynamicOps") && m.getReturnType().getSimpleName().contains("RegistryOps")) {
256+
createContext = lookup().unreflect(m).bindTo(registryAccessEmpty);
257+
break;
258+
}
259+
}
260+
}
261+
}
262+
if (CLASS_COMPONENT_SERIALIZATION != null) {
263+
for (Field f : CLASS_COMPONENT_SERIALIZATION.getDeclaredFields()) {
264+
if (Modifier.isStatic(f.getModifiers()) && f.getType().getSimpleName().equals("Codec")) {
265+
f.setAccessible(true);
266+
Object codecInstance = f.get(null);
267+
Class<?> codecClass = codecInstance.getClass();
268+
for (Method m : codecClass.getDeclaredMethods()) {
269+
if (m.getName().equals("decode")) {
270+
codecDecode = lookup().unreflect(m).bindTo(codecInstance);
271+
} else if (m.getName().equals("encode")) {
272+
codecEncode = lookup().unreflect(m).bindTo(codecInstance);
273+
}
274+
}
275+
break;
276+
}
277+
}
278+
}
279+
221280
} catch (final Throwable error) {
222281
INITIALIZATION_ERROR.set(new UnsupportedOperationException("Error occurred during initialization", error));
223282
}
224283

225284
MC_TEXT_GSON = gson;
285+
JSON_OPS_INSTANCE = jsonOpsInstance;
226286
JSON_PARSER_INSTANCE = jsonParserInstance;
227287
TEXT_SERIALIZER_DESERIALIZE = textSerializerDeserialize;
228288
TEXT_SERIALIZER_SERIALIZE = textSerializerSerialize;
229289
TEXT_SERIALIZER_DESERIALIZE_TREE = textSerializerDeserializeTree;
230290
TEXT_SERIALIZER_SERIALIZE_TREE = textSerializerSerializeTree;
291+
COMPONENTSERIALIZATION_CODEC_ENCODE = codecEncode;
292+
COMPONENTSERIALIZATION_CODEC_DECODE = codecDecode;
293+
REGISTRY_ACCESS_EMPTY = registryAccessEmpty;
294+
CREATE_SERIALIZATION_CONTEXT = createContext;
231295
}
232296

233-
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);
297+
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) || (COMPONENTSERIALIZATION_CODEC_ENCODE != null && COMPONENTSERIALIZATION_CODEC_DECODE != null && REGISTRY_ACCESS_EMPTY != null && CREATE_SERIALIZATION_CONTEXT != null && JSON_OPS_INSTANCE != null);
234298

235299
@Override
236300
public @NotNull Component deserialize(final @NotNull Object input) {
@@ -242,6 +306,12 @@ public static boolean isSupported() {
242306
element = TEXT_SERIALIZER_SERIALIZE_TREE.invoke(input);
243307
} else if (MC_TEXT_GSON != null) {
244308
element = ((Gson) MC_TEXT_GSON).toJsonTree(input);
309+
} else if (COMPONENTSERIALIZATION_CODEC_ENCODE != null && REGISTRY_ACCESS_EMPTY != null && CREATE_SERIALIZATION_CONTEXT != null) {
310+
final Object serializationContext = CREATE_SERIALIZATION_CONTEXT.invoke(REGISTRY_ACCESS_EMPTY, JSON_OPS_INSTANCE);
311+
Object result = COMPONENTSERIALIZATION_CODEC_ENCODE.invoke(serializationContext, input, null);
312+
Method getOrThrow = result.getClass().getMethod("getOrThrow", java.util.function.Function.class);
313+
Object jsonElement = getOrThrow.invoke(result, (java.util.function.Function<Throwable, RuntimeException>) RuntimeException::new);
314+
return gson().serializer().fromJson(jsonElement.toString(), Component.class);
245315
} else {
246316
return gson().deserialize((String) TEXT_SERIALIZER_SERIALIZE.invoke(input));
247317
}
@@ -268,6 +338,14 @@ public static boolean isSupported() {
268338
}
269339
} else {
270340
try {
341+
if (COMPONENTSERIALIZATION_CODEC_DECODE != null && REGISTRY_ACCESS_EMPTY != null && CREATE_SERIALIZATION_CONTEXT != null) {
342+
final Object serializationContext = CREATE_SERIALIZATION_CONTEXT.invoke(JSON_OPS_INSTANCE);
343+
Object result = COMPONENTSERIALIZATION_CODEC_DECODE.invoke(serializationContext, gson().serializeToTree(component));
344+
Method getOrThrow = result.getClass().getMethod("getOrThrow", java.util.function.Function.class);
345+
Object pair = getOrThrow.invoke(result, (java.util.function.Function<Throwable, RuntimeException>) RuntimeException::new);
346+
Method getFirst = pair.getClass().getMethod("getFirst");
347+
return getFirst.invoke(pair);
348+
}
271349
return TEXT_SERIALIZER_DESERIALIZE.invoke(gson().serialize(component));
272350
} catch (final Throwable error) {
273351
throw new UnsupportedOperationException(error);

0 commit comments

Comments
 (0)