29
29
import java .lang .reflect .Field ;
30
30
import java .lang .reflect .Method ;
31
31
import java .lang .reflect .Modifier ;
32
+ import java .lang .reflect .ParameterizedType ;
33
+ import java .lang .reflect .Type ;
32
34
import java .util .ArrayList ;
33
35
import java .util .Arrays ;
34
36
import java .util .Comparator ;
@@ -95,7 +97,12 @@ public static boolean isSupported() {
95
97
findMcClassName ("network.chat.IChatBaseComponent" ),
96
98
findMcClassName ("network.chat.Component" )
97
99
);
100
+ private static final @ Nullable Class <?> CLASS_COMPONENT_SERIALIZATION = findClass (findMcClassName ("network.chat.ComponentSerialization" ));
98
101
private static final @ Nullable Class <?> CLASS_CRAFT_REGISTRY = findCraftClass ("CraftRegistry" );
102
+ private static final @ Nullable Class <?> CLASS_HOLDERLOOKUP_PROVIDER = findClass (
103
+ findMcClassName ("core.HolderLookup$Provider" ), // Paper mapping
104
+ findMcClassName ("core.HolderLookup$a" ) // Spigot mapping
105
+ );
99
106
private static final @ Nullable Class <?> CLASS_REGISTRY_ACCESS = findClass (
100
107
findMcClassName ("core.IRegistryCustom" ),
101
108
findMcClassName ("core.RegistryAccess" )
@@ -109,6 +116,10 @@ public static boolean isSupported() {
109
116
private static final MethodHandle TEXT_SERIALIZER_SERIALIZE ;
110
117
private static final MethodHandle TEXT_SERIALIZER_DESERIALIZE_TREE ;
111
118
private static final MethodHandle TEXT_SERIALIZER_SERIALIZE_TREE ;
119
+ private static final MethodHandle COMPONENTSERIALIZATION_CODEC_ENCODE ;
120
+ private static final MethodHandle COMPONENTSERIALIZATION_CODEC_DECODE ;
121
+ private static final Object REGISTRY_ACCESS_EMPTY ;
122
+ private static final MethodHandle CREATE_SERIALIZATION_CONTEXT ;
112
123
113
124
static {
114
125
Object gson = null ;
@@ -117,6 +128,10 @@ public static boolean isSupported() {
117
128
MethodHandle textSerializerSerialize = null ;
118
129
MethodHandle textSerializerDeserializeTree = null ;
119
130
MethodHandle textSerializerSerializeTree = null ;
131
+ MethodHandle codecEncode = null ;
132
+ MethodHandle codecDecode = null ;
133
+ Object registryAccessEmpty = null ;
134
+ MethodHandle createContext = null ;
120
135
121
136
try {
122
137
if (CLASS_JSON_PARSER != null ) {
@@ -218,6 +233,42 @@ public static boolean isSupported() {
218
233
}
219
234
}
220
235
}
236
+ if (CLASS_REGISTRY_ACCESS != null ) {
237
+ for (Field field : CLASS_REGISTRY_ACCESS .getDeclaredFields ()) {
238
+ field .setAccessible (true );
239
+ if (Modifier .isStatic (field .getModifiers ()) && CLASS_REGISTRY_ACCESS .isAssignableFrom (field .getType ())) {
240
+ registryAccessEmpty = field .get (null );
241
+ break ;
242
+ }
243
+ }
244
+ if (registryAccessEmpty != null && CLASS_HOLDERLOOKUP_PROVIDER != null ) {
245
+ for (Method m : CLASS_HOLDERLOOKUP_PROVIDER .getDeclaredMethods ()) {
246
+ m .setAccessible (true );
247
+ if (m .getParameterCount () == 1 && m .getParameterTypes ()[0 ].getSimpleName ().equals ("DynamicOps" ) && m .getReturnType ().getSimpleName ().contains ("RegistryOps" )) {
248
+ createContext = lookup ().unreflect (m ).bindTo (registryAccessEmpty );
249
+ break ;
250
+ }
251
+ }
252
+ }
253
+ }
254
+ if (CLASS_COMPONENT_SERIALIZATION != null ) {
255
+ for (Field f : CLASS_COMPONENT_SERIALIZATION .getDeclaredFields ()) {
256
+ if (Modifier .isStatic (f .getModifiers ()) && f .getType ().getSimpleName ().equals ("Codec" )) {
257
+ f .setAccessible (true );
258
+ Object codecInstance = f .get (null );
259
+ Class <?> codecClass = codecInstance .getClass ();
260
+ for (Method m : codecClass .getDeclaredMethods ()) {
261
+ if (m .getName ().equals ("decode" )) {
262
+ codecDecode = lookup ().unreflect (m ).bindTo (codecInstance );
263
+ } else if (m .getName ().equals ("encode" )) {
264
+ codecEncode = lookup ().unreflect (m ).bindTo (codecInstance );
265
+ }
266
+ }
267
+ break ;
268
+ }
269
+ }
270
+ }
271
+
221
272
} catch (final Throwable error ) {
222
273
INITIALIZATION_ERROR .set (new UnsupportedOperationException ("Error occurred during initialization" , error ));
223
274
}
@@ -228,9 +279,13 @@ public static boolean isSupported() {
228
279
TEXT_SERIALIZER_SERIALIZE = textSerializerSerialize ;
229
280
TEXT_SERIALIZER_DESERIALIZE_TREE = textSerializerDeserializeTree ;
230
281
TEXT_SERIALIZER_SERIALIZE_TREE = textSerializerSerializeTree ;
282
+ COMPONENTSERIALIZATION_CODEC_ENCODE = codecEncode ;
283
+ COMPONENTSERIALIZATION_CODEC_DECODE = codecDecode ;
284
+ REGISTRY_ACCESS_EMPTY = registryAccessEmpty ;
285
+ CREATE_SERIALIZATION_CONTEXT = createContext ;
231
286
}
232
287
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 );
288
+ 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 ) ;
234
289
235
290
@ Override
236
291
public @ NotNull Component deserialize (final @ NotNull Object input ) {
@@ -242,6 +297,13 @@ public static boolean isSupported() {
242
297
element = TEXT_SERIALIZER_SERIALIZE_TREE .invoke (input );
243
298
} else if (MC_TEXT_GSON != null ) {
244
299
element = ((Gson ) MC_TEXT_GSON ).toJsonTree (input );
300
+ } else if (COMPONENTSERIALIZATION_CODEC_ENCODE != null && REGISTRY_ACCESS_EMPTY != null && CREATE_SERIALIZATION_CONTEXT != null ) {
301
+ final Object jsonOps = Class .forName ("com.mojang.serialization.JsonOps" ).getField ("INSTANCE" ).get (null );
302
+ final Object serializationContext = CREATE_SERIALIZATION_CONTEXT .invoke (REGISTRY_ACCESS_EMPTY , jsonOps );
303
+ Object result = COMPONENTSERIALIZATION_CODEC_ENCODE .invoke (serializationContext , input , null );
304
+ Method getOrThrow = result .getClass ().getMethod ("getOrThrow" , java .util .function .Function .class );
305
+ Object jsonElement = getOrThrow .invoke (result , (java .util .function .Function <Throwable , RuntimeException >) RuntimeException ::new );
306
+ return gson ().serializer ().fromJson (jsonElement .toString (), Component .class );
245
307
} else {
246
308
return gson ().deserialize ((String ) TEXT_SERIALIZER_SERIALIZE .invoke (input ));
247
309
}
@@ -268,6 +330,15 @@ public static boolean isSupported() {
268
330
}
269
331
} else {
270
332
try {
333
+ if (COMPONENTSERIALIZATION_CODEC_DECODE != null && REGISTRY_ACCESS_EMPTY != null && CREATE_SERIALIZATION_CONTEXT != null ) {
334
+ final Object jsonOps = Class .forName ("com.mojang.serialization.JsonOps" ).getField ("INSTANCE" ).get (null );
335
+ final Object serializationContext = CREATE_SERIALIZATION_CONTEXT .invoke (jsonOps );
336
+ Object result = COMPONENTSERIALIZATION_CODEC_DECODE .invoke (serializationContext , gson ().serializeToTree (component ));
337
+ Method getOrThrow = result .getClass ().getMethod ("getOrThrow" , java .util .function .Function .class );
338
+ Object pair = getOrThrow .invoke (result , (java .util .function .Function <Throwable , RuntimeException >) RuntimeException ::new );
339
+ Method getFirst = pair .getClass ().getMethod ("getFirst" );
340
+ return getFirst .invoke (pair );
341
+ }
271
342
return TEXT_SERIALIZER_DESERIALIZE .invoke (gson ().serialize (component ));
272
343
} catch (final Throwable error ) {
273
344
throw new UnsupportedOperationException (error );
0 commit comments