mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-09-27 14:52:52 +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;
|
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.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
@ -40,13 +46,18 @@ import net.minecraft.server.Packet;
|
|||||||
*
|
*
|
||||||
* @author Kristian
|
* @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
|
// Current structure modifier
|
||||||
protected StructureModifier<Object> structureModifier;
|
protected transient StructureModifier<Object> structureModifier;
|
||||||
|
|
||||||
// Check whether or not certain classes exists
|
// Check whether or not certain classes exists
|
||||||
private static boolean hasWorldType = false;
|
private static boolean hasWorldType = false;
|
||||||
@ -54,6 +65,10 @@ public class PacketContainer {
|
|||||||
// The getEntity method
|
// The getEntity method
|
||||||
private static Method getEntity;
|
private static Method getEntity;
|
||||||
|
|
||||||
|
// Support for serialization
|
||||||
|
private static Method writeMethod;
|
||||||
|
private static Method readMethod;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
Class.forName("net.minecraft.server.WorldType");
|
Class.forName("net.minecraft.server.WorldType");
|
||||||
@ -291,4 +306,60 @@ public class PacketContainer {
|
|||||||
public int getID() {
|
public int getID() {
|
||||||
return id;
|
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;
|
package com.comphenix.protocol.events;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
import java.util.EventObject;
|
import java.util.EventObject;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@ -28,11 +31,11 @@ public class PacketEvent extends EventObject implements Cancellable {
|
|||||||
*/
|
*/
|
||||||
private static final long serialVersionUID = -5360289379097430620L;
|
private static final long serialVersionUID = -5360289379097430620L;
|
||||||
|
|
||||||
|
private transient Player player;
|
||||||
private PacketContainer packet;
|
private PacketContainer packet;
|
||||||
private Player player;
|
|
||||||
private boolean serverPacket;
|
private boolean serverPacket;
|
||||||
private boolean cancel;
|
private boolean cancel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use the static constructors to create instances of this event.
|
* Use the static constructors to create instances of this event.
|
||||||
* @param source - the event source.
|
* @param source - the event source.
|
||||||
@ -125,4 +128,24 @@ public class PacketEvent extends EventObject implements Cancellable {
|
|||||||
public boolean isServerPacket() {
|
public boolean isServerPacket() {
|
||||||
return serverPacket;
|
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