5151import java .lang .reflect .Method ;
5252import java .time .Duration ;
5353import java .time .Instant ;
54+ import java .util .ArrayList ;
5455import java .util .Date ;
56+ import java .util .HashSet ;
57+ import java .util .List ;
5558import java .util .Map ;
59+ import java .util .Set ;
5660import java .util .UUID ;
5761import java .util .concurrent .ConcurrentHashMap ;
5862
6165 *
6266 * @author Kristian
6367 */
64- class SerializedOfflinePlayer implements OfflinePlayer , Serializable {
68+ abstract class SerializedOfflinePlayer implements OfflinePlayer , Serializable {
6569
6670 /**
6771 * Generated by Eclipse.
@@ -84,11 +88,25 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
8488 private long lastSeen ;
8589
8690 private static final Constructor <?> proxyPlayerConstructor = setupProxyPlayerConstructor ();
91+ private static final Constructor <? extends SerializedOfflinePlayer > CLASS_CONSTRUCTOR = setupClassConstructor ();
92+
93+ public static SerializedOfflinePlayer init (OfflinePlayer player ) {
94+ try {
95+ CLASS_CONSTRUCTOR .setAccessible (true );
96+ return CLASS_CONSTRUCTOR .newInstance (player );
97+ } catch (IllegalAccessException e ) {
98+ throw new RuntimeException ("Cannot access reflection." , e );
99+ } catch (InstantiationException e ) {
100+ throw new RuntimeException ("Cannot instantiate object." , e );
101+ } catch (InvocationTargetException e ) {
102+ throw new RuntimeException ("Error in invocation." , e );
103+ }
104+ }
87105
88106 /**
89107 * Constructor used by serialization.
90108 */
91- public SerializedOfflinePlayer () {
109+ protected SerializedOfflinePlayer () {
92110 // Do nothing
93111 }
94112
@@ -97,7 +115,7 @@ public SerializedOfflinePlayer() {
97115 *
98116 * @param offline - another player.
99117 */
100- public SerializedOfflinePlayer (OfflinePlayer offline ) {
118+ protected SerializedOfflinePlayer (OfflinePlayer offline ) {
101119 this .name = offline .getName ();
102120 this .uuid = offline .getUniqueId ();
103121 this .firstPlayed = offline .getFirstPlayed ();
@@ -342,6 +360,47 @@ public Player getProxyPlayer() {
342360 }
343361 }
344362
363+ private static Constructor <? extends SerializedOfflinePlayer > setupClassConstructor () {
364+ final Method [] existingMethods = SerializedOfflinePlayer .class .getDeclaredMethods ();
365+ final Set <String > existingMethodNames = new HashSet <>();
366+
367+ for (int idx = 0 ; idx < existingMethods .length ; idx ++) {
368+ existingMethodNames .add (existingMethods [idx ].getName ());
369+ }
370+
371+ final Method [] offlinePlayerMethods = OfflinePlayer .class .getMethods ();
372+ final List <String > methodNamesToAdd = new ArrayList <>();
373+
374+ for (int idx = 0 ; idx < offlinePlayerMethods .length ; idx ++) {
375+ final String name = offlinePlayerMethods [idx ].getName ();
376+
377+ if (!existingMethodNames .contains (name )) {
378+ methodNamesToAdd .add (name );
379+ }
380+ }
381+
382+ final ElementMatcher .Junction <ByteCodeElement > missingMethods =
383+ ElementMatchers .namedOneOf (methodNamesToAdd .toArray (new String [methodNamesToAdd .size ()]));
384+
385+ final InvocationHandlerAdapter throwException = InvocationHandlerAdapter .of ((obj , method , args ) -> {
386+ throw new UnsupportedOperationException (
387+ "The method " + method .getName () + " is not supported." );
388+ });
389+
390+ try {
391+ return ByteBuddyFactory .getInstance ()
392+ .createSubclass (SerializedOfflinePlayer .class )
393+ .method (missingMethods )
394+ .intercept (throwException )
395+ .make ()
396+ .load (ByteBuddyFactory .getInstance ().getClassLoader (), ClassLoadingStrategy .Default .INJECTION )
397+ .getLoaded ()
398+ .getConstructor (OfflinePlayer .class );
399+ } catch (NoSuchMethodException ex ) {
400+ throw new RuntimeException ("Failed to find SerializedOfflinePlayer constructor!" , ex );
401+ }
402+ }
403+
345404 private static Constructor <? extends Player > setupProxyPlayerConstructor () {
346405 final Method [] offlinePlayerMethods = OfflinePlayer .class .getMethods ();
347406 final String [] methodNames = new String [offlinePlayerMethods .length ];
0 commit comments