mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2025-04-08 21:25:50 +02:00
Add support for Java serialization of PacketEvent. It might be useful.
This commit is contained in:
parent
7f69c0204d
commit
e0c03186c3
@ -17,6 +17,12 @@
|
||||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@ -40,13 +46,18 @@ import net.minecraft.server.Packet;
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PacketContainer {
|
||||
public class PacketContainer implements Serializable {
|
||||
|
||||
protected Packet handle;
|
||||
protected int id;
|
||||
/**
|
||||
* Generated by Eclipse.
|
||||
*/
|
||||
private static final long serialVersionUID = 2074805748222377230L;
|
||||
|
||||
protected int id;
|
||||
protected transient Packet handle;
|
||||
|
||||
// Current structure modifier
|
||||
protected StructureModifier<Object> structureModifier;
|
||||
protected transient StructureModifier<Object> structureModifier;
|
||||
|
||||
// Check whether or not certain classes exists
|
||||
private static boolean hasWorldType = false;
|
||||
@ -54,6 +65,10 @@ public class PacketContainer {
|
||||
// The getEntity method
|
||||
private static Method getEntity;
|
||||
|
||||
// Support for serialization
|
||||
private static Method writeMethod;
|
||||
private static Method readMethod;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("net.minecraft.server.WorldType");
|
||||
@ -291,4 +306,60 @@ public class PacketContainer {
|
||||
public int getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream output) throws IOException {
|
||||
// Default serialization
|
||||
output.defaultWriteObject();
|
||||
|
||||
// We'll take care of NULL packets as well
|
||||
output.writeBoolean(handle != null);
|
||||
|
||||
// Retrieve the write method by reflection
|
||||
if (writeMethod == null)
|
||||
writeMethod = FuzzyReflection.fromObject(handle).getMethodByParameters("write", DataOutputStream.class);
|
||||
|
||||
try {
|
||||
// Call the write-method
|
||||
writeMethod.invoke(handle, new DataOutputStream(output));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Minecraft packet doesn't support DataOutputStream", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Insufficient security privileges.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IOException("Could not serialize Minecraft packet.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
|
||||
// Default deserialization
|
||||
input.defaultReadObject();
|
||||
|
||||
// Get structure modifier
|
||||
structureModifier = StructureCache.getStructure(id);
|
||||
|
||||
// Don't read NULL packets
|
||||
if (input.readBoolean()) {
|
||||
|
||||
// Create a default instance of the packet
|
||||
handle = StructureCache.newPacket(id);
|
||||
|
||||
// Retrieve the read method by reflection
|
||||
if (readMethod == null)
|
||||
readMethod = FuzzyReflection.fromObject(handle).getMethodByParameters("read", DataInputStream.class);
|
||||
|
||||
// Call the read method
|
||||
try {
|
||||
readMethod.invoke(handle, new DataInputStream(input));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Minecraft packet doesn't support DataInputStream", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Insufficient security privileges.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IOException("Could not deserialize Minecraft packet.", e);
|
||||
}
|
||||
|
||||
// And we're done
|
||||
structureModifier = structureModifier.withTarget(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,9 @@
|
||||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.EventObject;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
@ -28,11 +31,11 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
*/
|
||||
private static final long serialVersionUID = -5360289379097430620L;
|
||||
|
||||
private transient Player player;
|
||||
private PacketContainer packet;
|
||||
private Player player;
|
||||
private boolean serverPacket;
|
||||
private boolean cancel;
|
||||
|
||||
|
||||
/**
|
||||
* Use the static constructors to create instances of this event.
|
||||
* @param source - the event source.
|
||||
@ -125,4 +128,24 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
public boolean isServerPacket() {
|
||||
return serverPacket;
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream output) throws IOException {
|
||||
// Default serialization
|
||||
output.defaultWriteObject();
|
||||
|
||||
// Write the name of the player (or NULL if it's not set)
|
||||
output.writeObject(player != null ? new SerializedOfflinePlayer(player) : null);
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
|
||||
// Default deserialization
|
||||
input.defaultReadObject();
|
||||
|
||||
final SerializedOfflinePlayer offlinePlayer = (SerializedOfflinePlayer) input.readObject();
|
||||
|
||||
if (offlinePlayer != null) {
|
||||
// Better than nothing
|
||||
player = offlinePlayer.getProxyPlayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,216 @@
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents a player object that can be serialized by Java.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
||||
|
||||
/**
|
||||
* Generated by Eclipse.
|
||||
*/
|
||||
private static final long serialVersionUID = -2728976288470282810L;
|
||||
|
||||
private transient Location bedSpawnLocation;
|
||||
|
||||
// Relevant data about an offline player
|
||||
private String name;
|
||||
private long firstPlayed;
|
||||
private long lastPlayed;
|
||||
private boolean operator;
|
||||
private boolean banned;
|
||||
private boolean playedBefore;
|
||||
private boolean online;
|
||||
private boolean whitelisted;
|
||||
|
||||
// Proxy helper
|
||||
private static Map<String, Method> lookup = new ConcurrentHashMap<String, Method>();
|
||||
|
||||
/**
|
||||
* Constructor used by serialization.
|
||||
*/
|
||||
public SerializedOfflinePlayer() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this serializable offline player from another player.
|
||||
* @param offline - another player.
|
||||
*/
|
||||
public SerializedOfflinePlayer(OfflinePlayer offline) {
|
||||
this.name = offline.getName();
|
||||
this.firstPlayed = offline.getFirstPlayed();
|
||||
this.lastPlayed = offline.getLastPlayed();
|
||||
this.operator = offline.isOp();
|
||||
this.banned = offline.isBanned();
|
||||
this.playedBefore = offline.hasPlayedBefore();
|
||||
this.online = offline.isOnline();
|
||||
this.whitelisted = offline.isWhitelisted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOp() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOp(boolean operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getBedSpawnLocation() {
|
||||
return bedSpawnLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFirstPlayed() {
|
||||
return firstPlayed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastPlayed() {
|
||||
return lastPlayed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPlayedBefore() {
|
||||
return playedBefore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBanned() {
|
||||
return banned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBanned(boolean banned) {
|
||||
this.banned = banned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return online;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted() {
|
||||
return whitelisted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhitelisted(boolean whitelisted) {
|
||||
this.whitelisted = whitelisted;
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream output) throws IOException {
|
||||
output.defaultWriteObject();
|
||||
|
||||
// Serialize the bed spawn location
|
||||
output.writeUTF(bedSpawnLocation.getWorld().getName());
|
||||
output.writeDouble(bedSpawnLocation.getX());
|
||||
output.writeDouble(bedSpawnLocation.getY());
|
||||
output.writeDouble(bedSpawnLocation.getZ());
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
|
||||
input.defaultReadObject();
|
||||
|
||||
// Well, this is a problem
|
||||
bedSpawnLocation = new Location(
|
||||
getWorld(input.readUTF()),
|
||||
input.readDouble(),
|
||||
input.readDouble(),
|
||||
input.readDouble()
|
||||
);
|
||||
}
|
||||
|
||||
private World getWorld(String name) {
|
||||
try {
|
||||
// Try to get the world at least
|
||||
return Bukkit.getServer().getWorld(name);
|
||||
} catch (Exception e) {
|
||||
// Screw it
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
try {
|
||||
// Try to get the real player underneath
|
||||
return Bukkit.getServer().getPlayerExact(name);
|
||||
} catch (Exception e) {
|
||||
return getProxyPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a player object that implements OfflinePlayer by refering to this object.
|
||||
* <p>
|
||||
* All other methods cause an exception.
|
||||
* @return Proxy object.
|
||||
*/
|
||||
public Player getProxyPlayer() {
|
||||
|
||||
// Remember to initialize the method filter
|
||||
if (lookup.size() == 0) {
|
||||
// Add all public methods
|
||||
for (Method method : OfflinePlayer.class.getMethods()) {
|
||||
lookup.put(method.getName(), method);
|
||||
}
|
||||
}
|
||||
|
||||
// MORE CGLIB magic!
|
||||
Enhancer ex = new Enhancer();
|
||||
ex.setSuperclass(Player.class);
|
||||
ex.setCallback(new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
|
||||
// There's no overloaded methods, so we don't care
|
||||
Method offlineMethod = lookup.get(method.getName());
|
||||
|
||||
// Ignore all other methods
|
||||
if (offlineMethod == null) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The method " + method.getName() + " is not supported for offline players.");
|
||||
}
|
||||
|
||||
// Invoke our on method
|
||||
return offlineMethod.invoke(SerializedOfflinePlayer.this, args);
|
||||
}
|
||||
});
|
||||
|
||||
return (Player) ex.create();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user