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 ;
@@ -89,36 +91,57 @@ public static boolean isSupported() {
89
91
90
92
private static final @ Nullable Class <?> CLASS_JSON_DESERIALIZER = findClass ("com.goo" .concat ("gle.gson.JsonDeserializer" )); // Hide from relocation checkers
91
93
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" ));
92
95
private static final @ Nullable Class <?> CLASS_JSON_PARSER = findClass ("com.goo" .concat ("gle.gson.JsonParser" ));
93
96
private static final @ Nullable Class <?> CLASS_CHAT_COMPONENT = findClass (
94
97
findNmsClassName ("IChatBaseComponent" ),
95
98
findMcClassName ("network.chat.IChatBaseComponent" ),
96
99
findMcClassName ("network.chat.Component" )
97
100
);
101
+ private static final @ Nullable Class <?> CLASS_COMPONENT_SERIALIZATION = findClass (findMcClassName ("network.chat.ComponentSerialization" ));
98
102
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
+ );
99
107
private static final @ Nullable Class <?> CLASS_REGISTRY_ACCESS = findClass (
100
108
findMcClassName ("core.IRegistryCustom" ),
101
109
findMcClassName ("core.RegistryAccess" )
102
110
);
103
111
private static final @ Nullable MethodHandle PARSE_JSON = findMethod (CLASS_JSON_PARSER , "parse" , CLASS_JSON_ELEMENT , String .class );
104
112
private static final @ Nullable MethodHandle GET_REGISTRY = findStaticMethod (CLASS_CRAFT_REGISTRY , "getMinecraftRegistry" , CLASS_REGISTRY_ACCESS );
105
113
private static final AtomicReference <RuntimeException > INITIALIZATION_ERROR = new AtomicReference <>(new UnsupportedOperationException ());
114
+ private static final Object JSON_OPS_INSTANCE ;
106
115
private static final Object JSON_PARSER_INSTANCE ;
107
116
private static final Object MC_TEXT_GSON ;
108
117
private static final MethodHandle TEXT_SERIALIZER_DESERIALIZE ;
109
118
private static final MethodHandle TEXT_SERIALIZER_SERIALIZE ;
110
119
private static final MethodHandle TEXT_SERIALIZER_DESERIALIZE_TREE ;
111
120
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 ;
112
125
113
126
static {
114
127
Object gson = null ;
128
+ Object jsonOpsInstance = null ;
115
129
Object jsonParserInstance = null ;
116
130
MethodHandle textSerializerDeserialize = null ;
117
131
MethodHandle textSerializerSerialize = null ;
118
132
MethodHandle textSerializerDeserializeTree = null ;
119
133
MethodHandle textSerializerSerializeTree = null ;
134
+ MethodHandle codecEncode = null ;
135
+ MethodHandle codecDecode = null ;
136
+ Object registryAccessEmpty = null ;
137
+ MethodHandle createContext = null ;
120
138
121
139
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
+ }
122
145
if (CLASS_JSON_PARSER != null ) {
123
146
jsonParserInstance = CLASS_JSON_PARSER .getDeclaredConstructor ().newInstance ();
124
147
}
@@ -218,19 +241,60 @@ public static boolean isSupported() {
218
241
}
219
242
}
220
243
}
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
+
221
280
} catch (final Throwable error ) {
222
281
INITIALIZATION_ERROR .set (new UnsupportedOperationException ("Error occurred during initialization" , error ));
223
282
}
224
283
225
284
MC_TEXT_GSON = gson ;
285
+ JSON_OPS_INSTANCE = jsonOpsInstance ;
226
286
JSON_PARSER_INSTANCE = jsonParserInstance ;
227
287
TEXT_SERIALIZER_DESERIALIZE = textSerializerDeserialize ;
228
288
TEXT_SERIALIZER_SERIALIZE = textSerializerSerialize ;
229
289
TEXT_SERIALIZER_DESERIALIZE_TREE = textSerializerDeserializeTree ;
230
290
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 ;
231
295
}
232
296
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 ) ;
234
298
235
299
@ Override
236
300
public @ NotNull Component deserialize (final @ NotNull Object input ) {
@@ -242,6 +306,12 @@ public static boolean isSupported() {
242
306
element = TEXT_SERIALIZER_SERIALIZE_TREE .invoke (input );
243
307
} else if (MC_TEXT_GSON != null ) {
244
308
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 );
245
315
} else {
246
316
return gson ().deserialize ((String ) TEXT_SERIALIZER_SERIALIZE .invoke (input ));
247
317
}
@@ -268,6 +338,14 @@ public static boolean isSupported() {
268
338
}
269
339
} else {
270
340
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
+ }
271
349
return TEXT_SERIALIZER_DESERIALIZE .invoke (gson ().serialize (component ));
272
350
} catch (final Throwable error ) {
273
351
throw new UnsupportedOperationException (error );
0 commit comments