51
51
import java .lang .reflect .Method ;
52
52
import java .time .Duration ;
53
53
import java .time .Instant ;
54
+ import java .util .ArrayList ;
54
55
import java .util .Date ;
56
+ import java .util .HashSet ;
57
+ import java .util .List ;
55
58
import java .util .Map ;
59
+ import java .util .Set ;
56
60
import java .util .UUID ;
57
61
import java .util .concurrent .ConcurrentHashMap ;
58
62
61
65
*
62
66
* @author Kristian
63
67
*/
64
- class SerializedOfflinePlayer implements OfflinePlayer , Serializable {
68
+ abstract class SerializedOfflinePlayer implements OfflinePlayer , Serializable {
65
69
66
70
/**
67
71
* Generated by Eclipse.
@@ -84,11 +88,33 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
84
88
private long lastSeen ;
85
89
86
90
private static final Constructor <?> proxyPlayerConstructor = setupProxyPlayerConstructor ();
91
+ private static final Constructor <? extends SerializedOfflinePlayer > CLASS_CONSTRUCTOR = setupClassConstructor ();
92
+
93
+ /**
94
+ * Initialize a serializable offline player object from another offline player.
95
+ * <p>
96
+ * All other methods cause an exception.
97
+ *
98
+ * @param player - another offline player.
99
+ * @return A serializable offline player object.
100
+ */
101
+ public static SerializedOfflinePlayer init (OfflinePlayer player ) {
102
+ try {
103
+ CLASS_CONSTRUCTOR .setAccessible (true );
104
+ return CLASS_CONSTRUCTOR .newInstance (player );
105
+ } catch (IllegalAccessException e ) {
106
+ throw new RuntimeException ("Cannot access reflection." , e );
107
+ } catch (InstantiationException e ) {
108
+ throw new RuntimeException ("Cannot instantiate object." , e );
109
+ } catch (InvocationTargetException e ) {
110
+ throw new RuntimeException ("Error in invocation." , e );
111
+ }
112
+ }
87
113
88
114
/**
89
115
* Constructor used by serialization.
90
116
*/
91
- public SerializedOfflinePlayer () {
117
+ protected SerializedOfflinePlayer () {
92
118
// Do nothing
93
119
}
94
120
@@ -97,7 +123,7 @@ public SerializedOfflinePlayer() {
97
123
*
98
124
* @param offline - another player.
99
125
*/
100
- public SerializedOfflinePlayer (OfflinePlayer offline ) {
126
+ protected SerializedOfflinePlayer (OfflinePlayer offline ) {
101
127
this .name = offline .getName ();
102
128
this .uuid = offline .getUniqueId ();
103
129
this .firstPlayed = offline .getFirstPlayed ();
@@ -342,6 +368,47 @@ public Player getProxyPlayer() {
342
368
}
343
369
}
344
370
371
+ private static Constructor <? extends SerializedOfflinePlayer > setupClassConstructor () {
372
+ final Method [] existingMethods = SerializedOfflinePlayer .class .getDeclaredMethods ();
373
+ final Set <String > existingMethodNames = new HashSet <>();
374
+
375
+ for (int idx = 0 ; idx < existingMethods .length ; idx ++) {
376
+ existingMethodNames .add (existingMethods [idx ].getName ());
377
+ }
378
+
379
+ final Method [] offlinePlayerMethods = OfflinePlayer .class .getMethods ();
380
+ final List <String > methodNamesToAdd = new ArrayList <>();
381
+
382
+ for (int idx = 0 ; idx < offlinePlayerMethods .length ; idx ++) {
383
+ final String name = offlinePlayerMethods [idx ].getName ();
384
+
385
+ if (!existingMethodNames .contains (name )) {
386
+ methodNamesToAdd .add (name );
387
+ }
388
+ }
389
+
390
+ final ElementMatcher .Junction <ByteCodeElement > missingMethods =
391
+ ElementMatchers .namedOneOf (methodNamesToAdd .toArray (new String [methodNamesToAdd .size ()]));
392
+
393
+ final InvocationHandlerAdapter throwException = InvocationHandlerAdapter .of ((obj , method , args ) -> {
394
+ throw new UnsupportedOperationException (
395
+ "The method " + method .getName () + " is not supported." );
396
+ });
397
+
398
+ try {
399
+ return ByteBuddyFactory .getInstance ()
400
+ .createSubclass (SerializedOfflinePlayer .class )
401
+ .method (missingMethods )
402
+ .intercept (throwException )
403
+ .make ()
404
+ .load (ByteBuddyFactory .getInstance ().getClassLoader (), ClassLoadingStrategy .Default .INJECTION )
405
+ .getLoaded ()
406
+ .getConstructor (OfflinePlayer .class );
407
+ } catch (NoSuchMethodException ex ) {
408
+ throw new RuntimeException ("Failed to find SerializedOfflinePlayer constructor!" , ex );
409
+ }
410
+ }
411
+
345
412
private static Constructor <? extends Player > setupProxyPlayerConstructor () {
346
413
final Method [] offlinePlayerMethods = OfflinePlayer .class .getMethods ();
347
414
final String [] methodNames = new String [offlinePlayerMethods .length ];
0 commit comments