generate missing methods in SerializedOfflinePlayer at runtime
This commit is contained in:
parent
80aa420099
commit
cd45943cdd
|
@ -527,7 +527,7 @@ public class PacketEvent extends EventObject implements Cancellable {
|
|||
|
||||
// Write the name of the player (or NULL if it's not set)
|
||||
Player player = getPlayer();
|
||||
output.writeObject(player != null ? new SerializedOfflinePlayer(player) : null);
|
||||
output.writeObject(player != null ? SerializedOfflinePlayer.init(player) : null);
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
|
||||
|
|
|
@ -51,8 +51,12 @@ import java.lang.reflect.InvocationTargetException;
|
|||
import java.lang.reflect.Method;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
@ -61,7 +65,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
||||
abstract class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
||||
|
||||
/**
|
||||
* Generated by Eclipse.
|
||||
|
@ -84,11 +88,33 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
|||
private long lastSeen;
|
||||
|
||||
private static final Constructor<?> proxyPlayerConstructor = setupProxyPlayerConstructor();
|
||||
private static final Constructor<? extends SerializedOfflinePlayer> CLASS_CONSTRUCTOR = setupClassConstructor();
|
||||
|
||||
/**
|
||||
* Initialize a serializable offline player object from another offline player.
|
||||
* <p>
|
||||
* All other methods cause an exception.
|
||||
*
|
||||
* @param player - another offline player.
|
||||
* @return A serializable offline player object.
|
||||
*/
|
||||
public static SerializedOfflinePlayer init(OfflinePlayer player) {
|
||||
try {
|
||||
CLASS_CONSTRUCTOR.setAccessible(true);
|
||||
return CLASS_CONSTRUCTOR.newInstance(player);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot access reflection.", e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException("Cannot instantiate object.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Error in invocation.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor used by serialization.
|
||||
*/
|
||||
public SerializedOfflinePlayer() {
|
||||
protected SerializedOfflinePlayer() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
@ -97,7 +123,7 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
|||
*
|
||||
* @param offline - another player.
|
||||
*/
|
||||
public SerializedOfflinePlayer(OfflinePlayer offline) {
|
||||
protected SerializedOfflinePlayer(OfflinePlayer offline) {
|
||||
this.name = offline.getName();
|
||||
this.uuid = offline.getUniqueId();
|
||||
this.firstPlayed = offline.getFirstPlayed();
|
||||
|
@ -342,6 +368,47 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
private static Constructor<? extends SerializedOfflinePlayer> setupClassConstructor() {
|
||||
final Method[] existingMethods = SerializedOfflinePlayer.class.getDeclaredMethods();
|
||||
final Set<String> existingMethodNames = new HashSet<>();
|
||||
|
||||
for (int idx = 0; idx < existingMethods.length; idx++) {
|
||||
existingMethodNames.add(existingMethods[idx].getName());
|
||||
}
|
||||
|
||||
final Method[] offlinePlayerMethods = OfflinePlayer.class.getMethods();
|
||||
final List<String> methodNamesToAdd = new ArrayList<>();
|
||||
|
||||
for (int idx = 0; idx < offlinePlayerMethods.length; idx++) {
|
||||
final String name = offlinePlayerMethods[idx].getName();
|
||||
|
||||
if (!existingMethodNames.contains(name)) {
|
||||
methodNamesToAdd.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
final ElementMatcher.Junction<ByteCodeElement> missingMethods =
|
||||
ElementMatchers.namedOneOf(methodNamesToAdd.toArray(new String[methodNamesToAdd.size()]));
|
||||
|
||||
final InvocationHandlerAdapter throwException = InvocationHandlerAdapter.of((obj, method, args) -> {
|
||||
throw new UnsupportedOperationException(
|
||||
"The method " + method.getName() + " is not supported.");
|
||||
});
|
||||
|
||||
try {
|
||||
return ByteBuddyFactory.getInstance()
|
||||
.createSubclass(SerializedOfflinePlayer.class)
|
||||
.method(missingMethods)
|
||||
.intercept(throwException)
|
||||
.make()
|
||||
.load(ByteBuddyFactory.getInstance().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
|
||||
.getLoaded()
|
||||
.getConstructor(OfflinePlayer.class);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
throw new RuntimeException("Failed to find SerializedOfflinePlayer constructor!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Constructor<? extends Player> setupProxyPlayerConstructor() {
|
||||
final Method[] offlinePlayerMethods = OfflinePlayer.class.getMethods();
|
||||
final String[] methodNames = new String[offlinePlayerMethods.length];
|
||||
|
|
|
@ -39,7 +39,7 @@ public class SerializedOfflinePlayerTest {
|
|||
when(offlinePlayer.hasPlayedBefore()).thenReturn(playedBefore);
|
||||
when(offlinePlayer.isWhitelisted()).thenReturn(whitelisted);
|
||||
|
||||
serializedOfflinePlayer = new SerializedOfflinePlayer(offlinePlayer);
|
||||
serializedOfflinePlayer = SerializedOfflinePlayer.init(offlinePlayer);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue