|
|
|
@ -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();
|
|
|
|
@ -145,6 +171,16 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
|
|
|
|
return lastSeen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Location getRespawnLocation() {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Location getLocation() {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO do we need to implement this?
|
|
|
|
|
|
|
|
|
|
public void incrementStatistic(Statistic statistic) throws IllegalArgumentException {
|
|
|
|
@ -342,6 +378,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];
|
|
|
|
|