34
34
import com .comphenix .protocol .utility .MinecraftMethods ;
35
35
import com .comphenix .protocol .utility .MinecraftProtocolVersion ;
36
36
import com .comphenix .protocol .utility .MinecraftReflection ;
37
+ import com .comphenix .protocol .utility .ObjectReconstructor ;
38
+ import com .comphenix .protocol .wrappers .Pair ;
37
39
import com .comphenix .protocol .wrappers .WrappedGameProfile ;
38
40
import com .google .common .base .Preconditions ;
39
41
import io .netty .buffer .ByteBuf ;
48
50
import org .bukkit .Bukkit ;
49
51
import org .bukkit .entity .Player ;
50
52
53
+ import java .lang .reflect .Field ;
51
54
import java .lang .reflect .InvocationTargetException ;
52
55
import java .lang .reflect .Method ;
53
56
import java .net .Socket ;
54
57
import java .net .SocketAddress ;
55
58
import java .util .*;
56
59
import java .util .Map .Entry ;
57
60
import java .util .concurrent .Callable ;
61
+ import java .util .concurrent .ConcurrentHashMap ;
58
62
import java .util .concurrent .atomic .AtomicInteger ;
59
-
60
63
/**
61
64
* Represents a channel injector.
62
65
* @author Kristian
@@ -88,8 +91,23 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
88
91
} catch (Exception ex ) {
89
92
throw new RuntimeException ("Encountered an error caused by a reload! Please properly restart your server!" , ex );
90
93
}
94
+
95
+ Method hiddenClassMethod = null ;
96
+ try {
97
+ if (Float .parseFloat (System .getProperty ("java.class.version" )) >= 59 ) {
98
+ hiddenClassMethod = Class .class .getMethod ("isHidden" );
99
+ }
100
+ } catch (NoSuchMethodException ignored ) {
101
+
102
+ }
103
+ IS_HIDDEN_CLASS = hiddenClassMethod ;
91
104
}
92
105
106
+ // Starting in Java 15 (59), the lambdas are hidden classes and we cannot use reflection to update
107
+ // the values anymore. Instead, the object will have to be reconstructed.
108
+ private static final Map <Class <?>, ObjectReconstructor <?>> RECONSTRUCTORS = new ConcurrentHashMap <>();
109
+ private static final Method IS_HIDDEN_CLASS ;
110
+
93
111
// Saved accessors
94
112
private Method decodeBuffer ;
95
113
private Method encodeBuffer ;
@@ -295,39 +313,39 @@ public ChannelPipeline pipeline() {
295
313
296
314
@ Override
297
315
protected <T > Callable <T > onMessageScheduled (final Callable <T > callable , FieldAccessor packetAccessor ) {
298
- final PacketEvent event = handleScheduled (callable , packetAccessor );
316
+ Pair < Callable < T >, PacketEvent > handled = handleScheduled (callable , packetAccessor );
299
317
300
318
// Handle cancelled events
301
- if (event != null && event .isCancelled ())
319
+ if (handled . getSecond () != null && handled . getSecond () .isCancelled ())
302
320
return null ;
303
321
304
322
return () -> {
305
323
T result ;
306
324
307
325
// This field must only be updated in the pipeline thread
308
- currentEvent = event ;
309
- result = callable .call ();
326
+ currentEvent = handled . getSecond () ;
327
+ result = handled . getFirst () .call ();
310
328
currentEvent = null ;
311
329
return result ;
312
330
};
313
331
}
314
332
315
333
@ Override
316
334
protected Runnable onMessageScheduled (final Runnable runnable , FieldAccessor packetAccessor ) {
317
- final PacketEvent event = handleScheduled (runnable , packetAccessor );
335
+ Pair < Runnable , PacketEvent > handled = handleScheduled (runnable , packetAccessor );
318
336
319
337
// Handle cancelled events
320
- if (event != null && event .isCancelled ())
338
+ if (handled . getSecond () != null && handled . getSecond () .isCancelled ())
321
339
return null ;
322
340
323
341
return () -> {
324
- currentEvent = event ;
325
- runnable .run ();
342
+ currentEvent = handled . getSecond () ;
343
+ handled . getFirst () .run ();
326
344
currentEvent = null ;
327
345
};
328
346
}
329
347
330
- PacketEvent handleScheduled (Object instance , FieldAccessor accessor ) {
348
+ < T > Pair < T , PacketEvent > handleScheduled (T instance , FieldAccessor accessor ) {
331
349
// Let the filters handle this packet
332
350
Object original = accessor .get (instance );
333
351
@@ -338,9 +356,9 @@ PacketEvent handleScheduled(Object instance, FieldAccessor accessor) {
338
356
if (marker != null ) {
339
357
PacketEvent result = new PacketEvent (ChannelInjector .class );
340
358
result .setNetworkMarker (marker );
341
- return result ;
359
+ return new Pair <>( instance , result ) ;
342
360
} else {
343
- return BYPASSED_PACKET ;
361
+ return new Pair <>( instance , BYPASSED_PACKET ) ;
344
362
}
345
363
}
346
364
@@ -350,11 +368,12 @@ PacketEvent handleScheduled(Object instance, FieldAccessor accessor) {
350
368
351
369
// Change packet to be scheduled
352
370
if (original != changed ) {
353
- accessor .set (instance , changed );
371
+ instance = (T ) (isHiddenClass (instance .getClass ()) ?
372
+ updatePacketMessageReconstruct (instance , changed , accessor ) :
373
+ updatePacketMessageSetReflection (instance , changed , accessor ));
354
374
}
355
375
}
356
-
357
- return event != null ? event : BYPASSED_PACKET ;
376
+ return new Pair <>(instance , event != null ? event : BYPASSED_PACKET );
358
377
}
359
378
});
360
379
@@ -363,6 +382,30 @@ PacketEvent handleScheduled(Object instance, FieldAccessor accessor) {
363
382
}
364
383
}
365
384
385
+ /**
386
+ * Changes the packet in a packet message using a {@link FieldAccessor}.
387
+ */
388
+ private static Object updatePacketMessageSetReflection (Object instance , Object newPacket , FieldAccessor accessor ) {
389
+ accessor .set (instance , newPacket );
390
+ return instance ;
391
+ }
392
+
393
+ /**
394
+ * Changes the packet in a packet message using a {@link ObjectReconstructor}.
395
+ */
396
+ private static Object updatePacketMessageReconstruct (Object instance , Object newPacket , FieldAccessor accessor ) {
397
+ final ObjectReconstructor <?> objectReconstructor =
398
+ RECONSTRUCTORS .computeIfAbsent (instance .getClass (), ObjectReconstructor ::new );
399
+
400
+ final Object [] values = objectReconstructor .getValues (instance );
401
+ final Field [] fields = objectReconstructor .getFields ();
402
+ for (int idx = 0 ; idx < fields .length ; ++idx )
403
+ if (fields [idx ].equals (accessor .getField ()))
404
+ values [idx ] = newPacket ;
405
+
406
+ return objectReconstructor .reconstruct (values );
407
+ }
408
+
366
409
/**
367
410
* Determine if the given object is a compressor or decompressor.
368
411
* @param handler - object to test.
@@ -943,4 +986,15 @@ ChannelInjector getChannelInjector() {
943
986
public Channel getChannel () {
944
987
return originalChannel ;
945
988
}
989
+
990
+ private static boolean isHiddenClass (Class <?> clz ) {
991
+ if (IS_HIDDEN_CLASS == null ) {
992
+ return false ;
993
+ }
994
+ try {
995
+ return (Boolean ) IS_HIDDEN_CLASS .invoke (clz );
996
+ } catch (Exception e ) {
997
+ throw new RuntimeException ("Failed to determine whether class '" + clz .getName () + "' is hidden or not" , e );
998
+ }
999
+ }
946
1000
}
0 commit comments