Updating ProtocolLib for 1.7.2.
Still a work in progress.
Dieser Commit ist enthalten in:
Ursprung
28b4874c60
Commit
c137640fee
@ -231,7 +231,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bukkit</groupId>
|
<groupId>org.bukkit</groupId>
|
||||||
<artifactId>craftbukkit</artifactId>
|
<artifactId>craftbukkit</artifactId>
|
||||||
<version>1.6.4-R2.0</version>
|
<version>1.7.2-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -70,6 +70,7 @@ class CleanupStaticMembers {
|
|||||||
*/
|
*/
|
||||||
public void resetAll() {
|
public void resetAll() {
|
||||||
// This list must always be updated
|
// This list must always be updated
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
Class<?>[] publicClasses = {
|
Class<?>[] publicClasses = {
|
||||||
AsyncListenerHandler.class, ListeningWhitelist.class, PacketContainer.class,
|
AsyncListenerHandler.class, ListeningWhitelist.class, PacketContainer.class,
|
||||||
BukkitUnwrapper.class, DefaultInstances.class, CollectionGenerator.class,
|
BukkitUnwrapper.class, DefaultInstances.class, CollectionGenerator.class,
|
||||||
|
@ -1,8 +1,23 @@
|
|||||||
package com.comphenix.protocol;
|
package com.comphenix.protocol;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||||
import com.comphenix.protocol.reflect.ObjectEnum;
|
import com.comphenix.protocol.reflect.ObjectEnum;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the type of a packet in a specific protocol.
|
* Represents the type of a packet in a specific protocol.
|
||||||
@ -15,23 +30,66 @@ public class PacketType implements Serializable {
|
|||||||
// Increment whenever the type changes
|
// Increment whenever the type changes
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an unknown legacy packet ID.
|
||||||
|
*/
|
||||||
|
public static final int UNKNOWN_PACKET = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packets sent during handshake.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
public static class Handshake {
|
public static class Handshake {
|
||||||
public static final Protocol PROTOCOL = Protocol.HANDSHAKE;
|
private static final Protocol PROTOCOL = Protocol.HANDSHAKING;
|
||||||
|
|
||||||
public static class Client extends ObjectEnum<PacketType> {
|
public static class Client extends ObjectEnum<PacketType> {
|
||||||
public final static Sender SENDER = Sender.CLIENT;
|
private final static Sender SENDER = Sender.CLIENT;
|
||||||
public final static Client INSTANCE = new Client();
|
|
||||||
|
|
||||||
public static final PacketType HANDSHAKE = new PacketType(PROTOCOL, SENDER, 0x00, 2);
|
public static final PacketType HANDSHAKE = new PacketType(PROTOCOL, SENDER, 0x00, 2);
|
||||||
|
|
||||||
|
private final static Client INSTANCE = new Client();
|
||||||
|
|
||||||
|
// Prevent accidental construction
|
||||||
|
private Client() { super(PacketType.class); }
|
||||||
|
|
||||||
|
public static Client getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
public static Sender getSender() {
|
||||||
|
return SENDER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An empty enum, as the server will not send any packets in this protocol.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public static class Server extends ObjectEnum<PacketType> {
|
||||||
|
private final static Sender SENDER = Sender.CLIENT;
|
||||||
|
private final static Server INSTANCE = new Server();
|
||||||
|
private Server() { super(PacketType.class); }
|
||||||
|
|
||||||
|
public static Server getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
public static Sender getSender() {
|
||||||
|
return SENDER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Protocol getProtocol() {
|
||||||
|
return PROTOCOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Game {
|
/**
|
||||||
public static final Protocol PROTOCOL = Protocol.GAME;
|
* Packets sent and recieved when logged into the game.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public static class Play {
|
||||||
|
private static final Protocol PROTOCOL = Protocol.GAME;
|
||||||
|
|
||||||
public static class Server extends ObjectEnum<PacketType> {
|
public static class Server extends ObjectEnum<PacketType> {
|
||||||
public final static Sender SENDER = Sender.SERVER;
|
private final static Sender SENDER = Sender.SERVER;
|
||||||
public final static Server INSTANCE = new Server();
|
|
||||||
|
|
||||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x00, 0);
|
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x00, 0);
|
||||||
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x01, 1);
|
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x01, 1);
|
||||||
@ -83,7 +141,7 @@ public class PacketType implements Serializable {
|
|||||||
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x2F, 103);
|
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x2F, 103);
|
||||||
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x30, 104);
|
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x30, 104);
|
||||||
public static final PacketType CRAFT_PROGRESS_BAR = new PacketType(PROTOCOL, SENDER, 0x31, 105);
|
public static final PacketType CRAFT_PROGRESS_BAR = new PacketType(PROTOCOL, SENDER, 0x31, 105);
|
||||||
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x32, 106);
|
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x32, 106);
|
||||||
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x33, 130);
|
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x33, 130);
|
||||||
public static final PacketType ITEM_DATA = new PacketType(PROTOCOL, SENDER, 0x34, 131);
|
public static final PacketType ITEM_DATA = new PacketType(PROTOCOL, SENDER, 0x34, 131);
|
||||||
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x35, 132);
|
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x35, 132);
|
||||||
@ -99,11 +157,23 @@ public class PacketType implements Serializable {
|
|||||||
public static final PacketType SET_SCOREOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x3E, 209);
|
public static final PacketType SET_SCOREOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x3E, 209);
|
||||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x3F, 250);
|
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x3F, 250);
|
||||||
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x40, 255);
|
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x40, 255);
|
||||||
|
|
||||||
|
// The instance must
|
||||||
|
private final static Server INSTANCE = new Server();
|
||||||
|
|
||||||
|
// Prevent accidental construction
|
||||||
|
private Server() { super(PacketType.class); }
|
||||||
|
|
||||||
|
public static Sender getSender() {
|
||||||
|
return SENDER;
|
||||||
|
}
|
||||||
|
public static Server getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Client extends ObjectEnum<PacketType> {
|
public static class Client extends ObjectEnum<PacketType> {
|
||||||
public final static Sender SENDER = Sender.CLIENT;
|
private final static Sender SENDER = Sender.CLIENT;
|
||||||
public final static Client INSTANCE = new Client();
|
|
||||||
|
|
||||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x00, 0);
|
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x00, 0);
|
||||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x01, 3);
|
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x01, 3);
|
||||||
@ -129,51 +199,127 @@ public class PacketType implements Serializable {
|
|||||||
public static final PacketType LOCALE_AND_VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x15, 204);
|
public static final PacketType LOCALE_AND_VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x15, 204);
|
||||||
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x16, 205);
|
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x16, 205);
|
||||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x17, 250);
|
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x17, 250);
|
||||||
|
|
||||||
|
private final static Client INSTANCE = new Client();
|
||||||
|
|
||||||
|
// Prevent accidental construction
|
||||||
|
private Client() { super(PacketType.class); }
|
||||||
|
|
||||||
|
public static Sender getSender() {
|
||||||
|
return SENDER;
|
||||||
|
}
|
||||||
|
public static Client getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Protocol getProtocol() {
|
||||||
|
return PROTOCOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packets sent and recieved when querying the server in the multiplayer menu.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
public static class Status {
|
public static class Status {
|
||||||
public static final Protocol PROTOCOL = Protocol.STATUS;
|
private static final Protocol PROTOCOL = Protocol.STATUS;
|
||||||
|
|
||||||
public static class Server extends ObjectEnum<PacketType> {
|
public static class Server extends ObjectEnum<PacketType> {
|
||||||
public final static Sender SENDER = Sender.SERVER;
|
private final static Sender SENDER = Sender.SERVER;
|
||||||
public final static Server INSTANCE = new Server();
|
|
||||||
|
|
||||||
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, 255);
|
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, 255);
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public static final PacketType PING_TIME = new PacketType(PROTOCOL, SENDER, 0x00, Packets.Server.PING_TIME);
|
public static final PacketType PING_TIME = new PacketType(PROTOCOL, SENDER, 0x01, Packets.Server.PING_TIME);
|
||||||
|
|
||||||
|
private final static Server INSTANCE = new Server();
|
||||||
|
|
||||||
|
// Prevent accidental construction
|
||||||
|
private Server() { super(PacketType.class); }
|
||||||
|
|
||||||
|
public static Sender getSender() {
|
||||||
|
return SENDER;
|
||||||
|
}
|
||||||
|
public static Server getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Client extends ObjectEnum<PacketType> {
|
public static class Client extends ObjectEnum<PacketType> {
|
||||||
public final static Sender SENDER = Sender.CLIENT;
|
private final static Sender SENDER = Sender.CLIENT;
|
||||||
public final static Client INSTANCE = new Client();
|
|
||||||
|
|
||||||
public static final PacketType STATUS_REQUEST = new PacketType(PROTOCOL, SENDER, 0x00, 254);
|
public static final PacketType STATUS_REQUEST = new PacketType(PROTOCOL, SENDER, 0x00, 254);
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public static final PacketType PING_TIME = new PacketType(PROTOCOL, SENDER, 0x00, Packets.Client.PING_TIME);
|
public static final PacketType PING_TIME = new PacketType(PROTOCOL, SENDER, 0x01, Packets.Client.PING_TIME);
|
||||||
|
|
||||||
|
private final static Client INSTANCE = new Client();
|
||||||
|
|
||||||
|
// Prevent accidental construction
|
||||||
|
private Client() { super(PacketType.class); }
|
||||||
|
|
||||||
|
public static Sender getSender() {
|
||||||
|
return SENDER;
|
||||||
|
}
|
||||||
|
public static Client getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Protocol getProtocol() {
|
||||||
|
return PROTOCOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packets sent and recieved when logging in to the server.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
public static class Login {
|
public static class Login {
|
||||||
public static final Protocol PROTOCOL = Protocol.LOGIN;
|
private static final Protocol PROTOCOL = Protocol.LOGIN;
|
||||||
|
|
||||||
public static class Server extends ObjectEnum<PacketType> {
|
public static class Server extends ObjectEnum<PacketType> {
|
||||||
public final static Sender SENDER = Sender.SERVER;
|
private final static Sender SENDER = Sender.SERVER;
|
||||||
public final static Server INSTANCE = new Server();
|
|
||||||
|
|
||||||
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, 255);
|
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, 255);
|
||||||
public static final PacketType KEY_REQUEST = new PacketType(PROTOCOL, SENDER, 0x01, 253);
|
public static final PacketType KEY_REQUEST = new PacketType(PROTOCOL, SENDER, 0x01, 253);
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public static final PacketType LOGIN_SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, Packets.Server.LOGIN_SUCCESS);
|
public static final PacketType LOGIN_SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, Packets.Server.LOGIN_SUCCESS);
|
||||||
|
|
||||||
|
private final static Server INSTANCE = new Server();
|
||||||
|
|
||||||
|
// Prevent accidental construction
|
||||||
|
private Server() { super(PacketType.class); }
|
||||||
|
|
||||||
|
public static Sender getSender() {
|
||||||
|
return SENDER;
|
||||||
|
}
|
||||||
|
public static Server getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Client extends ObjectEnum<PacketType> {
|
public static class Client extends ObjectEnum<PacketType> {
|
||||||
public final static Sender SENDER = Sender.CLIENT;
|
private final static Sender SENDER = Sender.CLIENT;
|
||||||
public final static Client INSTANCE = new Client();
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public static final PacketType LOGIN_START = new PacketType(PROTOCOL, SENDER, 0x00, Packets.Client.LOGIN_START);
|
public static final PacketType LOGIN_START = new PacketType(PROTOCOL, SENDER, 0x00, Packets.Client.LOGIN_START);
|
||||||
public static final PacketType KEY_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x01, 252);
|
public static final PacketType KEY_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x01, 252);
|
||||||
|
|
||||||
|
private final static Client INSTANCE = new Client();
|
||||||
|
|
||||||
|
// Prevent accidental construction
|
||||||
|
private Client() { super(PacketType.class); }
|
||||||
|
|
||||||
|
public static Sender getSender() {
|
||||||
|
return SENDER;
|
||||||
|
}
|
||||||
|
public static Client getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Protocol getProtocol() {
|
||||||
|
return PROTOCOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,10 +328,29 @@ public class PacketType implements Serializable {
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public enum Protocol {
|
public enum Protocol {
|
||||||
HANDSHAKE,
|
HANDSHAKING,
|
||||||
GAME,
|
GAME,
|
||||||
STATUS,
|
STATUS,
|
||||||
LOGIN
|
LOGIN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the correct protocol enum from a given vanilla enum instance.
|
||||||
|
* @param vanilla - the vanilla protocol enum instance.
|
||||||
|
* @return The corresponding protocol.
|
||||||
|
*/
|
||||||
|
public static Protocol fromVanilla(Enum<?> vanilla) {
|
||||||
|
String name = vanilla.name();
|
||||||
|
|
||||||
|
if ("HANDSHAKING".equals(name))
|
||||||
|
return HANDSHAKING;
|
||||||
|
if ("PLAY".equals(name))
|
||||||
|
return GAME;
|
||||||
|
if ("STATUS".equals(name))
|
||||||
|
return STATUS;
|
||||||
|
if ("LOGIN".equals(name))
|
||||||
|
return LOGIN;
|
||||||
|
throw new IllegalArgumentException("Unrecognized vanilla enum " + vanilla);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -205,23 +370,189 @@ public class PacketType implements Serializable {
|
|||||||
SERVER
|
SERVER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lookup of packet types
|
||||||
|
private static PacketTypeLookup LOOKUP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protocol version of all the current IDs.
|
||||||
|
*/
|
||||||
|
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.WORLD_UPDATE;
|
||||||
|
|
||||||
private final Protocol protocol;
|
private final Protocol protocol;
|
||||||
private final Sender sender;
|
private final Sender sender;
|
||||||
private final int currentId;
|
private final int currentId;
|
||||||
private final int legacyId;
|
private final int legacyId;
|
||||||
|
private final MinecraftVersion version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the current packet/legacy lookup.
|
||||||
|
* @return The packet type lookup.
|
||||||
|
*/
|
||||||
|
private static PacketTypeLookup getLookup() {
|
||||||
|
if (LOOKUP == null) {
|
||||||
|
LOOKUP = new PacketTypeLookup().
|
||||||
|
addPacketTypes(Handshake.Client.getInstance()).
|
||||||
|
addPacketTypes(Handshake.Server.getInstance()).
|
||||||
|
addPacketTypes(Play.Client.getInstance()).
|
||||||
|
addPacketTypes(Play.Server.getInstance()).
|
||||||
|
addPacketTypes(Status.Client.getInstance()).
|
||||||
|
addPacketTypes(Status.Server.getInstance()).
|
||||||
|
addPacketTypes(Login.Client.getInstance()).
|
||||||
|
addPacketTypes(Login.Server.getInstance());
|
||||||
|
}
|
||||||
|
return LOOKUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find every packet type known to the current version of ProtocolLib.
|
||||||
|
* @return Every packet type.
|
||||||
|
*/
|
||||||
|
public static Iterable<PacketType> values() {
|
||||||
|
List<Iterable<? extends PacketType>> sources = Lists.newArrayList();
|
||||||
|
sources.add(Handshake.Client.getInstance());
|
||||||
|
sources.add(Handshake.Server.getInstance());
|
||||||
|
sources.add(Play.Client.getInstance());
|
||||||
|
sources.add(Play.Server.getInstance());
|
||||||
|
sources.add(Status.Client.getInstance());
|
||||||
|
sources.add(Status.Server.getInstance());
|
||||||
|
sources.add(Login.Client.getInstance());
|
||||||
|
sources.add(Login.Server.getInstance());
|
||||||
|
return Iterables.concat(sources);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a packet type from a legacy (1.6.4 and below) packet ID.
|
||||||
|
* @param packetId - the legacy packet ID.
|
||||||
|
* @return The corresponding packet type.
|
||||||
|
* @throws IllegalArgumentException If the legacy packet could not be found.
|
||||||
|
*/
|
||||||
|
public static PacketType findLegacy(int packetId) {
|
||||||
|
PacketType type = getLookup().getFromLegacy(packetId);
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
return type;
|
||||||
|
throw new IllegalArgumentException("Cannot find legacy packet " + packetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a packet type from a protocol, sender and packet ID.
|
||||||
|
* @param protocol - the current protocol.
|
||||||
|
* @param sender - the sender.
|
||||||
|
* @param packetId - the packet ID.
|
||||||
|
* @return The corresponding packet type.
|
||||||
|
* @throws IllegalArgumentException If the current packet could not be found.
|
||||||
|
*/
|
||||||
|
public static PacketType findCurrent(Protocol protocol, Sender sender, int packetId) {
|
||||||
|
PacketType type = getLookup().getFromCurrent(protocol, sender, packetId);
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
return type;
|
||||||
|
throw new IllegalArgumentException("Cannot find packet " + packetId +
|
||||||
|
"(Protocol: " + protocol + ", Sender: " + sender + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a packet type from a protocol, sender and packet ID.
|
||||||
|
* <p>
|
||||||
|
* The packet will automatically be registered if its missing.
|
||||||
|
* @param protocol - the current protocol.
|
||||||
|
* @param sender - the sender.
|
||||||
|
* @param packetId - the packet ID.
|
||||||
|
* @param legacyId - the legacy packet ID. Can be UNKNOWN_PACKET.
|
||||||
|
* @return The corresponding packet type.
|
||||||
|
*/
|
||||||
|
public static PacketType fromCurrent(Protocol protocol, Sender sender, int packetId, int legacyId) {
|
||||||
|
PacketType type = getLookup().getFromCurrent(protocol, sender, packetId);
|
||||||
|
|
||||||
|
if (type == null) {
|
||||||
|
type = new PacketType(protocol, sender, packetId, legacyId);
|
||||||
|
|
||||||
|
// Many may be scheduled, but only the first will be executed
|
||||||
|
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a particular packet type.
|
||||||
|
* <p>
|
||||||
|
* Note that the registration will be performed on the main thread.
|
||||||
|
* @param type - the type to register.
|
||||||
|
* @param name - the name of the packet.
|
||||||
|
* @return A future telling us if our instance was registrered.
|
||||||
|
*/
|
||||||
|
public static Future<Boolean> scheduleRegister(final PacketType type, final String name) {
|
||||||
|
Callable<Boolean> callable = new Callable<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public Boolean call() throws Exception {
|
||||||
|
ObjectEnum<PacketType> objEnum;
|
||||||
|
|
||||||
|
// A bit ugly, but performance is critical
|
||||||
|
switch (type.getProtocol()) {
|
||||||
|
case HANDSHAKING:
|
||||||
|
objEnum = type.isClient() ? Handshake.Client.getInstance() : Handshake.Server.getInstance(); break;
|
||||||
|
case GAME:
|
||||||
|
objEnum = type.isClient() ? Play.Client.getInstance() : Play.Server.getInstance(); break;
|
||||||
|
case STATUS:
|
||||||
|
objEnum = type.isClient() ? Status.Client.getInstance() : Status.Server.getInstance(); break;
|
||||||
|
case LOGIN:
|
||||||
|
objEnum = type.isClient() ? Login.Client.getInstance() : Login.Server.getInstance(); break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unexpected protocol: " + type.getProtocol());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objEnum.registerMember(type, name)) {
|
||||||
|
getLookup().addPacketTypes(Arrays.asList(type));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Execute in the main thread if possible
|
||||||
|
if (Bukkit.getServer() == null || Bukkit.isPrimaryThread()) {
|
||||||
|
try {
|
||||||
|
return Futures.immediateFuture(callable.call());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Futures.immediateFailedFuture(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ProtocolLibrary.getExecutorSync().submit(callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new packet type.
|
||||||
|
* @param protocol - the current protocol.
|
||||||
|
* @param target - the target - client or server.
|
||||||
|
* @param currentId - the current packet ID, or
|
||||||
|
* @param legacyId - the legacy packet ID.
|
||||||
|
*/
|
||||||
|
public PacketType(Protocol protocol, Sender sender, int currentId, int legacyId) {
|
||||||
|
this(protocol, sender, currentId, legacyId, PROTOCOL_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new packet type.
|
* Construct a new packet type.
|
||||||
* @param protocol - the current protocol.
|
* @param protocol - the current protocol.
|
||||||
* @param target - the target - client or server.
|
* @param target - the target - client or server.
|
||||||
* @param currentId - the current packet ID.
|
* @param currentId - the current packet ID.
|
||||||
* @param legacyId - the legacy packet ID.
|
* @param legacyId - the legacy packet ID.
|
||||||
|
* @param version - the version of the current ID.
|
||||||
*/
|
*/
|
||||||
public PacketType(Protocol protocol, Sender sender, int currentId, int legacyId) {
|
public PacketType(Protocol protocol, Sender sender, int currentId, int legacyId, MinecraftVersion version) {
|
||||||
this.protocol = protocol;
|
this.protocol = Preconditions.checkNotNull(protocol, "protocol cannot be NULL");
|
||||||
this.sender = sender;
|
this.sender = Preconditions.checkNotNull(sender, "sender cannot be NULL");
|
||||||
this.currentId = currentId;
|
this.currentId = currentId;
|
||||||
this.legacyId = legacyId;
|
this.legacyId = legacyId;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this packet is supported on the current server.
|
||||||
|
* @return Whether or not the packet is supported.
|
||||||
|
*/
|
||||||
|
public boolean isSupported() {
|
||||||
|
return PacketRegistry.isSupported(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -240,6 +571,22 @@ public class PacketType implements Serializable {
|
|||||||
return sender;
|
return sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this packet was sent by the client.
|
||||||
|
* @return TRUE if it was, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isClient() {
|
||||||
|
return sender == Sender.CLIENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this packet was sent by the server.
|
||||||
|
* @return TRUE if it was, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isServer() {
|
||||||
|
return sender == Sender.SERVER;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the current protocol ID for this packet type.
|
* Retrieve the current protocol ID for this packet type.
|
||||||
* <p>
|
* <p>
|
||||||
@ -251,10 +598,26 @@ public class PacketType implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the legacy (pre 1.7.2) protocol ID of the packet type.
|
* Retrieve the equivalent packet class.
|
||||||
|
* @return The packet class.
|
||||||
|
*/
|
||||||
|
public Class<?> getPacketClass() {
|
||||||
|
return PacketRegistry.getPacketClassFromType(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Minecraft version for the current ID.
|
||||||
|
* @return The Minecraft version.
|
||||||
|
*/
|
||||||
|
public MinecraftVersion getCurrentVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the legacy (1.6.4 or below) protocol ID of the packet type.
|
||||||
* <p>
|
* <p>
|
||||||
* This ID is globally unique.
|
* This ID is globally unique.
|
||||||
* @return The legacy ID.
|
* @return The legacy ID, or {@link #UNKNOWN_PACKET} if unknown.
|
||||||
*/
|
*/
|
||||||
public int getLegacyId() {
|
public int getLegacyId() {
|
||||||
return legacyId;
|
return legacyId;
|
||||||
@ -262,7 +625,7 @@ public class PacketType implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hashCode(protocol, sender, legacyId, currentId);
|
return Objects.hashCode(protocol, sender, currentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -281,6 +644,8 @@ public class PacketType implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Packet [protocol=" + protocol + ", sender=" + sender + ", legacyId=" + legacyId + ", currentId=" + currentId + "]";
|
Class<?> clazz = getPacketClass();
|
||||||
|
return (clazz != null ? clazz.getSimpleName() : "UNREGISTERED") +
|
||||||
|
" [" + protocol + ", " + sender + ", " + currentId + ", legacy: " + legacyId + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
package com.comphenix.protocol;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType.Protocol;
|
||||||
|
import com.comphenix.protocol.PacketType.Sender;
|
||||||
|
import com.comphenix.protocol.collections.IntegerMap;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a packet type based on its version and ID, optionally with protocol and sender too.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
class PacketTypeLookup {
|
||||||
|
private static class ProtocolSenderLookup {
|
||||||
|
// Unroll lookup for performance reasons
|
||||||
|
public final IntegerMap<PacketType> HANDSHAKE_CLIENT = IntegerMap.newMap();
|
||||||
|
public final IntegerMap<PacketType> HANDSHAKE_SERVER = IntegerMap.newMap();
|
||||||
|
public final IntegerMap<PacketType> GAME_CLIENT = IntegerMap.newMap();
|
||||||
|
public final IntegerMap<PacketType> GAME_SERVER = IntegerMap.newMap();
|
||||||
|
public final IntegerMap<PacketType> STATUS_CLIENT = IntegerMap.newMap();
|
||||||
|
public final IntegerMap<PacketType> STATUS_SERVER = IntegerMap.newMap();
|
||||||
|
public final IntegerMap<PacketType> LOGIN_CLIENT = IntegerMap.newMap();
|
||||||
|
public final IntegerMap<PacketType> LOGIN_SERVER = IntegerMap.newMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the correct integer map for a specific protocol and sender.
|
||||||
|
* @param protocol - the protocol.
|
||||||
|
* @param sender - the sender.
|
||||||
|
* @return The integer map of packets.
|
||||||
|
*/
|
||||||
|
public IntegerMap<PacketType> getMap(Protocol protocol, Sender sender) {
|
||||||
|
switch (protocol) {
|
||||||
|
case HANDSHAKING:
|
||||||
|
return sender == Sender.CLIENT ? HANDSHAKE_CLIENT : HANDSHAKE_SERVER;
|
||||||
|
case GAME:
|
||||||
|
return sender == Sender.CLIENT ? GAME_CLIENT : GAME_SERVER;
|
||||||
|
case STATUS:
|
||||||
|
return sender == Sender.CLIENT ? STATUS_CLIENT : STATUS_SERVER;
|
||||||
|
case LOGIN:
|
||||||
|
return sender == Sender.CLIENT ? LOGIN_CLIENT : LOGIN_SERVER;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unable to find protocol " + protocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Packet IDs from 1.6.4 and below
|
||||||
|
private final IntegerMap<PacketType> legacyLookup = new IntegerMap<PacketType>();
|
||||||
|
|
||||||
|
// Packets for 1.7.2
|
||||||
|
private final ProtocolSenderLookup currentLookup = new ProtocolSenderLookup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a collection of packet types to the lookup.
|
||||||
|
* @param types - the types to add.
|
||||||
|
*/
|
||||||
|
public PacketTypeLookup addPacketTypes(Iterable<? extends PacketType> types) {
|
||||||
|
Preconditions.checkNotNull(types, "types cannot be NULL");
|
||||||
|
|
||||||
|
for (PacketType type : types) {
|
||||||
|
int legacy = type.getLegacyId();
|
||||||
|
|
||||||
|
// Skip unknown legacy packets
|
||||||
|
if (legacy != PacketType.UNKNOWN_PACKET) {
|
||||||
|
legacyLookup.put(type.getLegacyId(), type);
|
||||||
|
}
|
||||||
|
currentLookup.getMap(type.getProtocol(), type.getSender()).put(type.getCurrentId(), type);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a packet type from a legacy (1.6.4 and below) packet ID.
|
||||||
|
* @param packetId - the legacy packet ID.
|
||||||
|
* @return The corresponding packet type, or NULL if not found.
|
||||||
|
*/
|
||||||
|
public PacketType getFromLegacy(int packetId) {
|
||||||
|
return legacyLookup.get(packetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a packet type from a protocol, sender and packet ID.
|
||||||
|
* @param protocol - the current protocol.
|
||||||
|
* @param sender - the sender.
|
||||||
|
* @param packetId - the packet ID.
|
||||||
|
* @return The corresponding packet type, or NULL if not found.
|
||||||
|
*/
|
||||||
|
public PacketType getFromCurrent(Protocol protocol, Sender sender, int packetId) {
|
||||||
|
return currentLookup.getMap(protocol, sender).get(packetId);
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,7 @@ import org.bukkit.command.PluginCommand;
|
|||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import com.comphenix.executors.BukkitExecutors;
|
||||||
import com.comphenix.protocol.async.AsyncFilterManager;
|
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||||
import com.comphenix.protocol.error.BasicErrorReporter;
|
import com.comphenix.protocol.error.BasicErrorReporter;
|
||||||
import com.comphenix.protocol.error.DelegatedErrorReporter;
|
import com.comphenix.protocol.error.DelegatedErrorReporter;
|
||||||
@ -54,6 +55,7 @@ import com.comphenix.protocol.utility.MinecraftVersion;
|
|||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main entry point for ProtocolLib.
|
* The main entry point for ProtocolLib.
|
||||||
@ -113,12 +115,16 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
|
|
||||||
// Metrics and statistisc
|
// Metrics and statistisc
|
||||||
private Statistics statistisc;
|
private Statistics statistisc;
|
||||||
|
|
||||||
|
// Executors
|
||||||
|
private static ListeningScheduledExecutorService executorAsync;
|
||||||
|
private static ListeningScheduledExecutorService executorSync;
|
||||||
|
|
||||||
// Structure compiler
|
// Structure compiler
|
||||||
private BackgroundCompiler backgroundCompiler;
|
private BackgroundCompiler backgroundCompiler;
|
||||||
|
|
||||||
// Used to clean up server packets that have expired.
|
// Used to clean up server packets that have expired. But mostly required to simulate
|
||||||
// But mostly required to simulate recieving client packets.
|
// recieving client packets.
|
||||||
private int asyncPacketTask = -1;
|
private int asyncPacketTask = -1;
|
||||||
private int tickCounter = 0;
|
private int tickCounter = 0;
|
||||||
private static final int ASYNC_PACKET_DELAY = 1;
|
private static final int ASYNC_PACKET_DELAY = 1;
|
||||||
@ -141,7 +147,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
private CommandProtocol commandProtocol;
|
private CommandProtocol commandProtocol;
|
||||||
private CommandPacket commandPacket;
|
private CommandPacket commandPacket;
|
||||||
private CommandFilter commandFilter;
|
private CommandFilter commandFilter;
|
||||||
|
|
||||||
// Whether or not disable is not needed
|
// Whether or not disable is not needed
|
||||||
private boolean skipDisable;
|
private boolean skipDisable;
|
||||||
|
|
||||||
@ -150,6 +156,10 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
// Load configuration
|
// Load configuration
|
||||||
logger = getLoggerSafely();
|
logger = getLoggerSafely();
|
||||||
|
|
||||||
|
// Initialize executors
|
||||||
|
executorAsync = BukkitExecutors.newAsynchronous(this);
|
||||||
|
executorSync = BukkitExecutors.newSynchronous(this);
|
||||||
|
|
||||||
// Add global parameters
|
// Add global parameters
|
||||||
DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this);
|
DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this);
|
||||||
reporter = getFilteredReporter(detailedReporter);
|
reporter = getFilteredReporter(detailedReporter);
|
||||||
@ -526,6 +536,9 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bukkit will shut down tasks on our executors
|
||||||
|
// ...
|
||||||
|
|
||||||
// Disable compiler
|
// Disable compiler
|
||||||
if (backgroundCompiler != null) {
|
if (backgroundCompiler != null) {
|
||||||
backgroundCompiler.shutdownAll();
|
backgroundCompiler.shutdownAll();
|
||||||
@ -605,4 +618,24 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
public Statistics getStatistics() {
|
public Statistics getStatistics() {
|
||||||
return statistisc;
|
return statistisc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an executor service for performing asynchronous tasks on the behalf of ProtocolLib.
|
||||||
|
* <p>
|
||||||
|
* Note that this service is NULL if ProtocolLib has not been initialized yet.
|
||||||
|
* @return The executor service, or NULL.
|
||||||
|
*/
|
||||||
|
public static ListeningScheduledExecutorService getExecutorAsync() {
|
||||||
|
return executorAsync;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an executor service for performing synchronous tasks (main thread) on the behalf of ProtocolLib.
|
||||||
|
* <p>
|
||||||
|
* Note that this service is NULL if ProtocolLib has not been initialized yet.
|
||||||
|
* @return The executor service, or NULL.
|
||||||
|
*/
|
||||||
|
public static ListeningScheduledExecutorService getExecutorSync() {
|
||||||
|
return executorSync;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,139 @@
|
|||||||
|
package com.comphenix.protocol.collections;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a very quick integer-based lookup map, with a fixed key space size.
|
||||||
|
* <p>
|
||||||
|
* Integers must be non-negative.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class IntegerMap<T> {
|
||||||
|
private T[] array;
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new integer map.
|
||||||
|
* @return A new integer map.
|
||||||
|
*/
|
||||||
|
public static <T> IntegerMap<T> newMap() {
|
||||||
|
return new IntegerMap<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new integer map with a default capacity.
|
||||||
|
*/
|
||||||
|
public IntegerMap() {
|
||||||
|
this(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new integer map with a given capacity.
|
||||||
|
* @param initialCapacity - the capacity.
|
||||||
|
*/
|
||||||
|
public IntegerMap(int initialCapacity) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T[] backingArray = (T[]) new Object[initialCapacity];
|
||||||
|
this.array = backingArray;
|
||||||
|
this.size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate an integer key with the given value.
|
||||||
|
* @param key - the integer key. Cannot be negative.
|
||||||
|
* @param value - the value. Cannot be NULL.
|
||||||
|
* @return The previous association, or NULL if not found.
|
||||||
|
*/
|
||||||
|
public T put(int key, T value) {
|
||||||
|
ensureCapacity(key);
|
||||||
|
|
||||||
|
T old = array[key];
|
||||||
|
array[key] = Preconditions.checkNotNull(value, "value cannot be NULL");
|
||||||
|
|
||||||
|
if (old == null)
|
||||||
|
size++;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an association from the map.
|
||||||
|
* @param key - the key of the association to remove.
|
||||||
|
* @return The old associated value, or NULL.
|
||||||
|
*/
|
||||||
|
public T remove(int key) {
|
||||||
|
T old = array[key];
|
||||||
|
array[key] = null;
|
||||||
|
|
||||||
|
if (old != null)
|
||||||
|
size--;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the backing array to fit the given key.
|
||||||
|
* @param key - the key.
|
||||||
|
*/
|
||||||
|
protected void ensureCapacity(int key) {
|
||||||
|
int newLength = array.length;
|
||||||
|
|
||||||
|
// Don't resize if the key fits
|
||||||
|
if (key < 0)
|
||||||
|
throw new IllegalArgumentException("Negative key values are not permitted.");
|
||||||
|
if (key < newLength)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (newLength <= key) {
|
||||||
|
int next = newLength * 2;
|
||||||
|
// Handle overflow
|
||||||
|
newLength = next > newLength ? next : Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
this.array = Arrays.copyOf(array, newLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the number of mappings in this map.
|
||||||
|
* @return The number of mapping.
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the value associated with a given key.
|
||||||
|
* @param key - the key.
|
||||||
|
* @return The value, or NULL if not found.
|
||||||
|
*/
|
||||||
|
public T get(int key) {
|
||||||
|
if (key >= 0 && key < array.length)
|
||||||
|
return array[key];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given key exists in the map.
|
||||||
|
* @param key - the key to check.
|
||||||
|
* @return TRUE if it does, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean containsKey(int key) {
|
||||||
|
return get(key) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the current map to an Integer map.
|
||||||
|
* @return The Integer map.
|
||||||
|
*/
|
||||||
|
public Map<Integer, Object> toMap() {
|
||||||
|
Map<Integer, Object> map = Maps.newHashMap();
|
||||||
|
|
||||||
|
for (int i = 0; i < array.length; i++) {
|
||||||
|
if (array[i] != null) {
|
||||||
|
map.put(i, array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,16 @@ public class NetworkMarker {
|
|||||||
// Cache serializer too
|
// Cache serializer too
|
||||||
private StreamSerializer serializer;
|
private StreamSerializer serializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new network marker.
|
||||||
|
* @param side - whether or not this marker belongs to a client or server packet.
|
||||||
|
* @param inputBuffer - the read serialized packet data.
|
||||||
|
*/
|
||||||
|
public NetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer) {
|
||||||
|
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
|
||||||
|
this.inputBuffer = Preconditions.checkNotNull(inputBuffer, "inputBuffer cannot be NULL.");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new network marker.
|
* Construct a new network marker.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -28,21 +28,22 @@ import java.io.Serializable;
|
|||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.util.io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.WorldType;
|
import org.bukkit.WorldType;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.concurrency.IntegerSet;
|
|
||||||
import com.comphenix.protocol.injector.StructureCache;
|
import com.comphenix.protocol.injector.StructureCache;
|
||||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
@ -58,6 +59,7 @@ import com.comphenix.protocol.reflect.cloning.SerializableCloner;
|
|||||||
import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters;
|
import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters;
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
import com.comphenix.protocol.utility.StreamSerializer;
|
import com.comphenix.protocol.utility.StreamSerializer;
|
||||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||||
@ -69,6 +71,7 @@ import com.comphenix.protocol.wrappers.nbt.NbtBase;
|
|||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a Minecraft packet indirectly.
|
* Represents a Minecraft packet indirectly.
|
||||||
@ -76,13 +79,9 @@ import com.google.common.collect.Maps;
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class PacketContainer implements Serializable {
|
public class PacketContainer implements Serializable {
|
||||||
|
private static final long serialVersionUID = 3;
|
||||||
/**
|
|
||||||
* Generated by Eclipse.
|
|
||||||
*/
|
|
||||||
private static final long serialVersionUID = 2074805748222377230L;
|
|
||||||
|
|
||||||
protected int id;
|
protected PacketType type;
|
||||||
protected transient Object handle;
|
protected transient Object handle;
|
||||||
|
|
||||||
// Current structure modifier
|
// Current structure modifier
|
||||||
@ -118,15 +117,15 @@ public class PacketContainer implements Serializable {
|
|||||||
build();
|
build();
|
||||||
|
|
||||||
// Packets that cannot be cloned by our default deep cloner
|
// Packets that cannot be cloned by our default deep cloner
|
||||||
private static final IntegerSet CLONING_UNSUPPORTED = new IntegerSet(Packets.PACKET_COUNT,
|
private static final Set<PacketType> CLONING_UNSUPPORTED = Sets.newHashSet(
|
||||||
Arrays.asList(Packets.Server.UPDATE_ATTRIBUTES));
|
PacketType.Play.Server.UPDATE_ATTRIBUTES, PacketType.Status.Server.KICK_DISCONNECT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a packet container for a new packet.
|
* Creates a packet container for a new packet.
|
||||||
* @param id - ID of the packet to create.
|
* @param id - ID of the packet to create.
|
||||||
*/
|
*/
|
||||||
public PacketContainer(int id) {
|
public PacketContainer(int id) {
|
||||||
this(id, StructureCache.newPacket(id));
|
this(PacketType.findLegacy(id), StructureCache.newPacket(PacketType.findLegacy(id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,7 +134,7 @@ public class PacketContainer implements Serializable {
|
|||||||
* @param handle - contained packet.
|
* @param handle - contained packet.
|
||||||
*/
|
*/
|
||||||
public PacketContainer(int id, Object handle) {
|
public PacketContainer(int id, Object handle) {
|
||||||
this(id, handle, StructureCache.getStructure(id).withTarget(handle));
|
this(PacketType.findLegacy(id), handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,10 +144,39 @@ public class PacketContainer implements Serializable {
|
|||||||
* @param structure - structure modifier.
|
* @param structure - structure modifier.
|
||||||
*/
|
*/
|
||||||
public PacketContainer(int id, Object handle, StructureModifier<Object> structure) {
|
public PacketContainer(int id, Object handle, StructureModifier<Object> structure) {
|
||||||
|
this(PacketType.findLegacy(id), handle, structure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a packet container for a new packet.
|
||||||
|
* @param type - the type of the packet to create.
|
||||||
|
*/
|
||||||
|
public PacketContainer(PacketType type) {
|
||||||
|
this(type, StructureCache.newPacket(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a packet container for an existing packet.
|
||||||
|
* @param id - ID of the given packet.
|
||||||
|
* @param handle - contained packet.
|
||||||
|
*/
|
||||||
|
public PacketContainer(PacketType type, Object handle) {
|
||||||
|
this(type, handle, StructureCache.getStructure(type).withTarget(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a packet container for an existing packet.
|
||||||
|
* @param id - ID of the given packet.
|
||||||
|
* @param handle - contained packet.
|
||||||
|
* @param structure - structure modifier.
|
||||||
|
*/
|
||||||
|
public PacketContainer(PacketType type, Object handle, StructureModifier<Object> structure) {
|
||||||
if (handle == null)
|
if (handle == null)
|
||||||
throw new IllegalArgumentException("handle cannot be null.");
|
throw new IllegalArgumentException("handle cannot be null.");
|
||||||
|
if (type == null)
|
||||||
|
throw new IllegalArgumentException("type cannot be null.");
|
||||||
|
|
||||||
this.id = id;
|
this.type = type;
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
this.structureModifier = structure;
|
this.structureModifier = structure;
|
||||||
}
|
}
|
||||||
@ -440,10 +468,21 @@ public class PacketContainer implements Serializable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the ID of this packet.
|
* Retrieves the ID of this packet.
|
||||||
|
* <p>
|
||||||
|
* Deprecated: Use {@link #getType()} instead.
|
||||||
* @return Packet ID.
|
* @return Packet ID.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public int getID() {
|
public int getID() {
|
||||||
return id;
|
return type.getLegacyId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the packet type of this packet.
|
||||||
|
* @return The packet type.
|
||||||
|
*/
|
||||||
|
public PacketType getType() {
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -473,12 +512,12 @@ public class PacketContainer implements Serializable {
|
|||||||
Object clonedPacket = null;
|
Object clonedPacket = null;
|
||||||
|
|
||||||
// Fall back on the alternative (but slower) method of reading and writing back the packet
|
// Fall back on the alternative (but slower) method of reading and writing back the packet
|
||||||
if (CLONING_UNSUPPORTED.contains(id)) {
|
if (CLONING_UNSUPPORTED.contains(type)) {
|
||||||
clonedPacket = SerializableCloner.clone(this).getHandle();
|
clonedPacket = SerializableCloner.clone(this).getHandle();
|
||||||
} else {
|
} else {
|
||||||
clonedPacket = DEEP_CLONER.clone(getHandle());
|
clonedPacket = DEEP_CLONER.clone(getHandle());
|
||||||
}
|
}
|
||||||
return new PacketContainer(getID(), clonedPacket);
|
return new PacketContainer(getType(), clonedPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
// To save space, we'll skip copying the inflated buffers in packet 51 and 56
|
// To save space, we'll skip copying the inflated buffers in packet 51 and 56
|
||||||
@ -511,10 +550,20 @@ public class PacketContainer implements Serializable {
|
|||||||
output.writeBoolean(handle != null);
|
output.writeBoolean(handle != null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Call the write-method
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
getMethodLazily(writeMethods, handle.getClass(), "write", DataOutput.class).
|
ByteBuf buffer = createPacketBuffer();
|
||||||
invoke(handle, new DataOutputStream(output));
|
MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, buffer);
|
||||||
|
|
||||||
|
output.writeInt(buffer.readableBytes());
|
||||||
|
buffer.readBytes(output, buffer.readableBytes());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Call the write-method
|
||||||
|
output.writeInt(-1);
|
||||||
|
getMethodLazily(writeMethods, handle.getClass(), "write", DataOutput.class).
|
||||||
|
invoke(handle, new DataOutputStream(output));
|
||||||
|
}
|
||||||
|
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new IOException("Minecraft packet doesn't support DataOutputStream", e);
|
throw new IOException("Minecraft packet doesn't support DataOutputStream", e);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
@ -529,19 +578,28 @@ public class PacketContainer implements Serializable {
|
|||||||
input.defaultReadObject();
|
input.defaultReadObject();
|
||||||
|
|
||||||
// Get structure modifier
|
// Get structure modifier
|
||||||
structureModifier = StructureCache.getStructure(id);
|
structureModifier = StructureCache.getStructure(type);
|
||||||
|
|
||||||
// Don't read NULL packets
|
// Don't read NULL packets
|
||||||
if (input.readBoolean()) {
|
if (input.readBoolean()) {
|
||||||
|
|
||||||
// Create a default instance of the packet
|
// Create a default instance of the packet
|
||||||
handle = StructureCache.newPacket(id);
|
handle = StructureCache.newPacket(type);
|
||||||
|
|
||||||
// Call the read method
|
// Call the read method
|
||||||
try {
|
try {
|
||||||
getMethodLazily(readMethods, handle.getClass(), "read", DataInput.class).
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
invoke(handle, new DataInputStream(input));
|
ByteBuf buffer = createPacketBuffer();
|
||||||
|
buffer.writeBytes(input, input.readInt());
|
||||||
|
|
||||||
|
MinecraftMethods.getPacketReadByteBufMethod().invoke(handle, buffer);
|
||||||
|
} else {
|
||||||
|
if (input.readInt() != -1)
|
||||||
|
throw new IllegalArgumentException("Cannot load a packet from 1.7.2 in 1.6.4.");
|
||||||
|
|
||||||
|
getMethodLazily(readMethods, handle.getClass(), "read", DataInput.class).
|
||||||
|
invoke(handle, new DataInputStream(input));
|
||||||
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new IOException("Minecraft packet doesn't support DataInputStream", e);
|
throw new IOException("Minecraft packet doesn't support DataInputStream", e);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
@ -555,6 +613,14 @@ public class PacketContainer implements Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new packet data serializer.
|
||||||
|
* @return The packet data serializer.
|
||||||
|
*/
|
||||||
|
private ByteBuf createPacketBuffer() {
|
||||||
|
return MinecraftReflection.getPacketDataSerializer(UnpooledByteBufAllocator.DEFAULT.buffer());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the cached method concurrently.
|
* Retrieve the cached method concurrently.
|
||||||
* @param lookup - a lazy lookup cache.
|
* @param lookup - a lazy lookup cache.
|
||||||
|
@ -25,6 +25,7 @@ import java.util.EventObject;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.Cancellable;
|
import org.bukkit.event.Cancellable;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.async.AsyncMarker;
|
import com.comphenix.protocol.async.AsyncMarker;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
@ -156,12 +157,23 @@ public class PacketEvent extends EventObject implements Cancellable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the packet ID.
|
* Retrieves the packet ID.
|
||||||
|
* <p>
|
||||||
|
* Deprecated: Use {@link #getPacketType()} instead.
|
||||||
* @return The current packet ID.
|
* @return The current packet ID.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public int getPacketID() {
|
public int getPacketID() {
|
||||||
return packet.getID();
|
return packet.getID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the packet type.
|
||||||
|
* @return The type.
|
||||||
|
*/
|
||||||
|
public PacketType getPacketType() {
|
||||||
|
return packet.getType();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves whether or not the packet should be cancelled.
|
* Retrieves whether or not the packet should be cancelled.
|
||||||
* @return TRUE if it should be cancelled, FALSE otherwise.
|
* @return TRUE if it should be cancelled, FALSE otherwise.
|
||||||
@ -181,7 +193,7 @@ public class PacketEvent extends EventObject implements Cancellable {
|
|||||||
if (networkMarker == null) {
|
if (networkMarker == null) {
|
||||||
if (isServerPacket()) {
|
if (isServerPacket()) {
|
||||||
networkMarker = new NetworkMarker(
|
networkMarker = new NetworkMarker(
|
||||||
serverPacket ? ConnectionSide.SERVER_SIDE : ConnectionSide.CLIENT_SIDE, null);
|
serverPacket ? ConnectionSide.SERVER_SIDE : ConnectionSide.CLIENT_SIDE, (byte[]) null);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Add the option ListenerOptions.INTERCEPT_INPUT_BUFFER to your listener.");
|
throw new IllegalStateException("Add the option ListenerOptions.INTERCEPT_INPUT_BUFFER to your listener.");
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,8 @@ import com.google.common.primitives.Primitives;
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class BukkitUnwrapper implements Unwrapper {
|
public class BukkitUnwrapper implements Unwrapper {
|
||||||
|
private static BukkitUnwrapper DEFAULT;
|
||||||
|
|
||||||
public static final ReportType REPORT_ILLEGAL_ARGUMENT = new ReportType("Illegal argument.");
|
public static final ReportType REPORT_ILLEGAL_ARGUMENT = new ReportType("Illegal argument.");
|
||||||
public static final ReportType REPORT_SECURITY_LIMITATION = new ReportType("Security limitation.");
|
public static final ReportType REPORT_SECURITY_LIMITATION = new ReportType("Security limitation.");
|
||||||
public static final ReportType REPORT_CANNOT_FIND_UNWRAP_METHOD = new ReportType("Cannot find method.");
|
public static final ReportType REPORT_CANNOT_FIND_UNWRAP_METHOD = new ReportType("Cannot find method.");
|
||||||
@ -56,6 +58,20 @@ public class BukkitUnwrapper implements Unwrapper {
|
|||||||
// The current error reporter
|
// The current error reporter
|
||||||
private final ErrorReporter reporter;
|
private final ErrorReporter reporter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the default instance of the Bukkit unwrapper.
|
||||||
|
* @return The default instance.
|
||||||
|
*/
|
||||||
|
public static BukkitUnwrapper getInstance() {
|
||||||
|
ErrorReporter currentReporter = ProtocolLibrary.getErrorReporter();
|
||||||
|
|
||||||
|
// Also recreate the unwrapper if the error reporter has changed
|
||||||
|
if (DEFAULT == null || DEFAULT.reporter != currentReporter) {
|
||||||
|
DEFAULT = new BukkitUnwrapper(currentReporter);
|
||||||
|
}
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new Bukkit unwrapper with ProtocolLib's default error reporter.
|
* Construct a new Bukkit unwrapper with ProtocolLib's default error reporter.
|
||||||
*/
|
*/
|
||||||
|
@ -44,6 +44,7 @@ public interface ListenerInvoker {
|
|||||||
* @param packet - the packet.
|
* @param packet - the packet.
|
||||||
* @return The packet ID.
|
* @return The packet ID.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract int getPacketID(Object packet);
|
public abstract int getPacketID(Object packet);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,6 +2,7 @@ package com.comphenix.protocol.injector;
|
|||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.Packets;
|
||||||
import com.comphenix.protocol.concurrency.IntegerSet;
|
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||||
import com.comphenix.protocol.events.ConnectionSide;
|
import com.comphenix.protocol.events.ConnectionSide;
|
||||||
@ -17,6 +18,7 @@ class LoginPackets {
|
|||||||
private IntegerSet clientSide = new IntegerSet(Packets.PACKET_COUNT);
|
private IntegerSet clientSide = new IntegerSet(Packets.PACKET_COUNT);
|
||||||
private IntegerSet serverSide = new IntegerSet(Packets.PACKET_COUNT);
|
private IntegerSet serverSide = new IntegerSet(Packets.PACKET_COUNT);
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public LoginPackets(MinecraftVersion version) {
|
public LoginPackets(MinecraftVersion version) {
|
||||||
// Ordinary login
|
// Ordinary login
|
||||||
clientSide.add(Packets.Client.HANDSHAKE);
|
clientSide.add(Packets.Client.HANDSHAKE);
|
||||||
@ -55,6 +57,7 @@ class LoginPackets {
|
|||||||
* @param side - the direction.
|
* @param side - the direction.
|
||||||
* @return TRUE if it may, FALSE otherwise.
|
* @return TRUE if it may, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public boolean isLoginPacket(int packetId, ConnectionSide side) {
|
public boolean isLoginPacket(int packetId, ConnectionSide side) {
|
||||||
switch (side) {
|
switch (side) {
|
||||||
case CLIENT_SIDE:
|
case CLIENT_SIDE:
|
||||||
@ -68,4 +71,16 @@ class LoginPackets {
|
|||||||
throw new IllegalArgumentException("Unknown connection side: " + side);
|
throw new IllegalArgumentException("Unknown connection side: " + side);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a given packet may be sent during login.
|
||||||
|
* @param type - the packet type.
|
||||||
|
* @return TRUE if it may, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isLoginPacket(PacketType type) {
|
||||||
|
return PacketType.Login.Client.getInstance().hasMember(type) ||
|
||||||
|
PacketType.Login.Server.getInstance().hasMember(type) ||
|
||||||
|
PacketType.Status.Client.getInstance().hasMember(type) ||
|
||||||
|
PacketType.Status.Server.getInstance().hasMember(type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import java.lang.reflect.Constructor;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.error.RethrowErrorReporter;
|
import com.comphenix.protocol.error.RethrowErrorReporter;
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||||
@ -36,7 +37,6 @@ import com.google.common.primitives.Primitives;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class PacketConstructor {
|
public class PacketConstructor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A packet constructor that automatically converts Bukkit types to their NMS conterpart.
|
* A packet constructor that automatically converts Bukkit types to their NMS conterpart.
|
||||||
* <p>
|
* <p>
|
||||||
@ -48,7 +48,7 @@ public class PacketConstructor {
|
|||||||
private Constructor<?> constructorMethod;
|
private Constructor<?> constructorMethod;
|
||||||
|
|
||||||
// The packet ID
|
// The packet ID
|
||||||
private int packetID;
|
private PacketType type;
|
||||||
|
|
||||||
// Used to unwrap Bukkit objects
|
// Used to unwrap Bukkit objects
|
||||||
private List<Unwrapper> unwrappers;
|
private List<Unwrapper> unwrappers;
|
||||||
@ -62,8 +62,8 @@ public class PacketConstructor {
|
|||||||
this.unwrappers.addAll(BukkitConverters.getUnwrappers());
|
this.unwrappers.addAll(BukkitConverters.getUnwrappers());
|
||||||
}
|
}
|
||||||
|
|
||||||
private PacketConstructor(int packetID, Constructor<?> constructorMethod, List<Unwrapper> unwrappers, Unwrapper[] paramUnwrapper) {
|
private PacketConstructor(PacketType type, Constructor<?> constructorMethod, List<Unwrapper> unwrappers, Unwrapper[] paramUnwrapper) {
|
||||||
this.packetID = packetID;
|
this.type = type;
|
||||||
this.constructorMethod = constructorMethod;
|
this.constructorMethod = constructorMethod;
|
||||||
this.unwrappers = unwrappers;
|
this.unwrappers = unwrappers;
|
||||||
this.paramUnwrapper = paramUnwrapper;
|
this.paramUnwrapper = paramUnwrapper;
|
||||||
@ -75,10 +75,21 @@ public class PacketConstructor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the id of the packets this constructor creates.
|
* Retrieve the id of the packets this constructor creates.
|
||||||
|
* <p>
|
||||||
|
* Deprecated: Use {@link #getType()} instead.
|
||||||
* @return The ID of the packets this constructor will create.
|
* @return The ID of the packets this constructor will create.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public int getPacketID() {
|
public int getPacketID() {
|
||||||
return packetID;
|
return type.getLegacyId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the type of the packets this constructor creates.
|
||||||
|
* @return The type of the created packets.
|
||||||
|
*/
|
||||||
|
public PacketType getType() {
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,19 +98,35 @@ public class PacketConstructor {
|
|||||||
* @return A constructor with a different set of unwrappers.
|
* @return A constructor with a different set of unwrappers.
|
||||||
*/
|
*/
|
||||||
public PacketConstructor withUnwrappers(List<Unwrapper> unwrappers) {
|
public PacketConstructor withUnwrappers(List<Unwrapper> unwrappers) {
|
||||||
return new PacketConstructor(packetID, constructorMethod, unwrappers, paramUnwrapper);
|
return new PacketConstructor(type, constructorMethod, unwrappers, paramUnwrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a packet constructor that creates packets using the given types.
|
* Create a packet constructor that creates packets using the given ID.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that if you pass a Class<?> as a value, it will use its type directly.
|
* Note that if you pass a Class<?> as a value, it will use its type directly.
|
||||||
* @param id - packet ID.
|
* <p>
|
||||||
|
* Deprecated: Use {@link #withPacket(PacketType, Object[])} instead.
|
||||||
|
* @param id - legacy (1.6.4) packet ID.
|
||||||
* @param values - the values that will match each parameter in the desired constructor.
|
* @param values - the values that will match each parameter in the desired constructor.
|
||||||
* @return A packet constructor with these types.
|
* @return A packet constructor with these types.
|
||||||
* @throws IllegalArgumentException If no packet constructor could be created with these types.
|
* @throws IllegalArgumentException If no packet constructor could be created with these types.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public PacketConstructor withPacket(int id, Object[] values) {
|
public PacketConstructor withPacket(int id, Object[] values) {
|
||||||
|
return withPacket(PacketType.findLegacy(id), values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a packet constructor that creates packets using the given types.
|
||||||
|
* <p>
|
||||||
|
* Note that if you pass a Class<?> as a value, it will use its type directly.
|
||||||
|
* @param type - the type of the packet to create.
|
||||||
|
* @param values - the values that will match each parameter in the desired constructor.
|
||||||
|
* @return A packet constructor with these types.
|
||||||
|
* @throws IllegalArgumentException If no packet constructor could be created with these types.
|
||||||
|
*/
|
||||||
|
public PacketConstructor withPacket(PacketType type, Object[] values) {
|
||||||
Class<?>[] types = new Class<?>[values.length];
|
Class<?>[] types = new Class<?>[values.length];
|
||||||
Throwable lastException = null;
|
Throwable lastException = null;
|
||||||
Unwrapper[] paramUnwrapper = new Unwrapper[values.length];
|
Unwrapper[] paramUnwrapper = new Unwrapper[values.length];
|
||||||
@ -131,11 +158,10 @@ public class PacketConstructor {
|
|||||||
types[i] = Object.class;
|
types[i] = Object.class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Class<?> packetType = PacketRegistry.getPacketClassFromType(type, true);
|
||||||
Class<?> packetType = PacketRegistry.getPacketClassFromID(id, true);
|
|
||||||
|
|
||||||
if (packetType == null)
|
if (packetType == null)
|
||||||
throw new IllegalArgumentException("Could not find a packet by the id " + id);
|
throw new IllegalArgumentException("Could not find a packet by the type " + type);
|
||||||
|
|
||||||
// Find the correct constructor
|
// Find the correct constructor
|
||||||
for (Constructor<?> constructor : packetType.getConstructors()) {
|
for (Constructor<?> constructor : packetType.getConstructors()) {
|
||||||
@ -143,10 +169,9 @@ public class PacketConstructor {
|
|||||||
|
|
||||||
if (isCompatible(types, params)) {
|
if (isCompatible(types, params)) {
|
||||||
// Right, we've found our type
|
// Right, we've found our type
|
||||||
return new PacketConstructor(id, constructor, unwrappers, paramUnwrapper);
|
return new PacketConstructor(type, constructor, unwrappers, paramUnwrapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalArgumentException("No suitable constructor could be found.", lastException);
|
throw new IllegalArgumentException("No suitable constructor could be found.", lastException);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +193,7 @@ public class PacketConstructor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Object nmsPacket = constructorMethod.newInstance(values);
|
Object nmsPacket = constructorMethod.newInstance(values);
|
||||||
return new PacketContainer(packetID, nmsPacket);
|
return new PacketContainer(type, nmsPacket);
|
||||||
|
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -57,6 +57,7 @@ import com.comphenix.protocol.error.ErrorReporter;
|
|||||||
import com.comphenix.protocol.error.Report;
|
import com.comphenix.protocol.error.Report;
|
||||||
import com.comphenix.protocol.error.ReportType;
|
import com.comphenix.protocol.error.ReportType;
|
||||||
import com.comphenix.protocol.events.*;
|
import com.comphenix.protocol.events.*;
|
||||||
|
import com.comphenix.protocol.injector.netty.NettyProtocolInjector;
|
||||||
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
|
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
|
||||||
import com.comphenix.protocol.injector.packet.PacketInjector;
|
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||||
import com.comphenix.protocol.injector.packet.PacketInjectorBuilder;
|
import com.comphenix.protocol.injector.packet.PacketInjectorBuilder;
|
||||||
@ -181,6 +182,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
// Spigot listener, if in use
|
// Spigot listener, if in use
|
||||||
private SpigotPacketInjector spigotInjector;
|
private SpigotPacketInjector spigotInjector;
|
||||||
|
|
||||||
|
// Netty injector (for 1.7.2)
|
||||||
|
private NettyProtocolInjector nettyInjector;
|
||||||
|
|
||||||
// Plugin verifier
|
// Plugin verifier
|
||||||
private PluginVerifier pluginVerifier;
|
private PluginVerifier pluginVerifier;
|
||||||
|
|
||||||
@ -231,7 +235,12 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
this.interceptWritePacket = new InterceptWritePacket(classLoader, reporter);
|
this.interceptWritePacket = new InterceptWritePacket(classLoader, reporter);
|
||||||
|
|
||||||
// Use the correct injection type
|
// Use the correct injection type
|
||||||
if (builder.isNettyEnabled()) {
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
|
this.nettyInjector = new NettyProtocolInjector(this);
|
||||||
|
this.playerInjection = nettyInjector.getPlayerInjector();
|
||||||
|
this.packetInjector = nettyInjector.getPacketInjector();
|
||||||
|
|
||||||
|
} else if (builder.isNettyEnabled()) {
|
||||||
this.spigotInjector = new SpigotPacketInjector(classLoader, reporter, this, server);
|
this.spigotInjector = new SpigotPacketInjector(classLoader, reporter, this, server);
|
||||||
this.playerInjection = spigotInjector.getPlayerHandler();
|
this.playerInjection = spigotInjector.getPlayerHandler();
|
||||||
this.packetInjector = spigotInjector.getPacketInjector();
|
this.packetInjector = spigotInjector.getPacketInjector();
|
||||||
@ -780,7 +789,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
|
|
||||||
if (filters) {
|
if (filters) {
|
||||||
byte[] data = NetworkMarker.getByteBuffer(marker);
|
byte[] data = NetworkMarker.getByteBuffer(marker);
|
||||||
PacketEvent event = packetInjector.packetRecieved(packet, sender, data);
|
PacketEvent event = packetInjector.packetRecieved(packet, sender, data);
|
||||||
|
|
||||||
if (!event.isCancelled())
|
if (!event.isCancelled())
|
||||||
mcPacket = event.getPacket().getHandle();
|
mcPacket = event.getPacket().getHandle();
|
||||||
@ -881,6 +890,8 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
public void registerEvents(PluginManager manager, final Plugin plugin) {
|
public void registerEvents(PluginManager manager, final Plugin plugin) {
|
||||||
if (spigotInjector != null && !spigotInjector.register(plugin))
|
if (spigotInjector != null && !spigotInjector.register(plugin))
|
||||||
throw new IllegalArgumentException("Spigot has already been registered.");
|
throw new IllegalArgumentException("Spigot has already been registered.");
|
||||||
|
if (nettyInjector != null)
|
||||||
|
nettyInjector.inject();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
manager.registerEvents(new Listener() {
|
manager.registerEvents(new Listener() {
|
||||||
@ -1009,7 +1020,6 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
// Yes, this is crazy.
|
// Yes, this is crazy.
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
private void registerOld(PluginManager manager, final Plugin plugin) {
|
private void registerOld(PluginManager manager, final Plugin plugin) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ClassLoader loader = manager.getClass().getClassLoader();
|
ClassLoader loader = manager.getClass().getClassLoader();
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||||
@ -35,19 +36,31 @@ import com.comphenix.protocol.utility.MinecraftReflection;
|
|||||||
*/
|
*/
|
||||||
public class StructureCache {
|
public class StructureCache {
|
||||||
// Structure modifiers
|
// Structure modifiers
|
||||||
private static ConcurrentMap<Integer, StructureModifier<Object>> structureModifiers =
|
private static ConcurrentMap<PacketType, StructureModifier<Object>> structureModifiers =
|
||||||
new ConcurrentHashMap<Integer, StructureModifier<Object>>();
|
new ConcurrentHashMap<PacketType, StructureModifier<Object>>();
|
||||||
|
|
||||||
private static Set<Integer> compiling = new HashSet<Integer>();
|
private static Set<PacketType> compiling = new HashSet<PacketType>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an empty Minecraft packet of the given ID.
|
* Creates an empty Minecraft packet of the given id.
|
||||||
* @param id - packet ID.
|
* <p>
|
||||||
|
* Decreated: Use {@link #newPacket(PacketType)} instead.
|
||||||
|
* @param legacyId - legacy (1.6.4) packet id.
|
||||||
* @return Created packet.
|
* @return Created packet.
|
||||||
*/
|
*/
|
||||||
public static Object newPacket(int id) {
|
@Deprecated
|
||||||
|
public static Object newPacket(int legacyId) {
|
||||||
|
return newPacket(PacketType.findLegacy(legacyId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an empty Minecraft packet of the given type.
|
||||||
|
* @param type - packet type.
|
||||||
|
* @return Created packet.
|
||||||
|
*/
|
||||||
|
public static Object newPacket(PacketType type) {
|
||||||
try {
|
try {
|
||||||
return PacketRegistry.getPacketClassFromID(id, true).newInstance();
|
return PacketRegistry.getPacketClassFromType(type, true).newInstance();
|
||||||
} catch (InstantiationException e) {
|
} catch (InstantiationException e) {
|
||||||
return null;
|
return null;
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
@ -57,12 +70,24 @@ public class StructureCache {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a cached structure modifier for the given packet id.
|
* Retrieve a cached structure modifier for the given packet id.
|
||||||
* @param id - packet ID.
|
* <p>
|
||||||
|
* Deprecated: Use {@link #getStructure(PacketType)} instead.
|
||||||
|
* @param legacyId - the legacy (1.6.4) packet ID.
|
||||||
* @return A structure modifier.
|
* @return A structure modifier.
|
||||||
*/
|
*/
|
||||||
public static StructureModifier<Object> getStructure(int id) {
|
@Deprecated
|
||||||
|
public static StructureModifier<Object> getStructure(int legacyId) {
|
||||||
|
return getStructure(PacketType.findLegacy(legacyId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a cached structure modifier for the given packet type.
|
||||||
|
* @param type - packet type.
|
||||||
|
* @return A structure modifier.
|
||||||
|
*/
|
||||||
|
public static StructureModifier<Object> getStructure(PacketType type) {
|
||||||
// Compile structures by default
|
// Compile structures by default
|
||||||
return getStructure(id, true);
|
return getStructure(type, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,26 +108,38 @@ public class StructureCache {
|
|||||||
*/
|
*/
|
||||||
public static StructureModifier<Object> getStructure(Class<?> packetType, boolean compile) {
|
public static StructureModifier<Object> getStructure(Class<?> packetType, boolean compile) {
|
||||||
// Get the ID from the class
|
// Get the ID from the class
|
||||||
return getStructure(PacketRegistry.getPacketID(packetType), compile);
|
return getStructure(PacketRegistry.getPacketType(packetType), compile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a cached structure modifier for the given packet id.
|
* Retrieve a cached structure modifier for the given packet ID.
|
||||||
* @param id - packet ID.
|
* <p>
|
||||||
|
* Deprecated: Use {@link #getStructure(PacketType, boolean)} instead.
|
||||||
|
* @param legacyId - the legacy (1.6.4) packet ID.
|
||||||
* @param compile - whether or not to asynchronously compile the structure modifier.
|
* @param compile - whether or not to asynchronously compile the structure modifier.
|
||||||
* @return A structure modifier.
|
* @return A structure modifier.
|
||||||
*/
|
*/
|
||||||
public static StructureModifier<Object> getStructure(int id, boolean compile) {
|
@Deprecated
|
||||||
|
public static StructureModifier<Object> getStructure(final int legacyId, boolean compile) {
|
||||||
StructureModifier<Object> result = structureModifiers.get(id);
|
return getStructure(PacketType.findLegacy(legacyId), compile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a cached structure modifier for the given packet type.
|
||||||
|
* @param type - packet type.
|
||||||
|
* @param compile - whether or not to asynchronously compile the structure modifier.
|
||||||
|
* @return A structure modifier.
|
||||||
|
*/
|
||||||
|
public static StructureModifier<Object> getStructure(final PacketType type, boolean compile) {
|
||||||
|
StructureModifier<Object> result = structureModifiers.get(type);
|
||||||
|
|
||||||
// We don't want to create this for every lookup
|
// We don't want to create this for every lookup
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
// Use the vanilla class definition
|
// Use the vanilla class definition
|
||||||
final StructureModifier<Object> value = new StructureModifier<Object>(
|
final StructureModifier<Object> value = new StructureModifier<Object>(
|
||||||
PacketRegistry.getPacketClassFromID(id, true), MinecraftReflection.getPacketClass(), true);
|
PacketRegistry.getPacketClassFromType(type, true), MinecraftReflection.getPacketClass(), true);
|
||||||
|
|
||||||
result = structureModifiers.putIfAbsent(id, value);
|
result = structureModifiers.putIfAbsent(type, value);
|
||||||
|
|
||||||
// We may end up creating multiple modifiers, but we'll agree on which to use
|
// We may end up creating multiple modifiers, but we'll agree on which to use
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
@ -114,21 +151,19 @@ public class StructureCache {
|
|||||||
if (compile && !(result instanceof CompiledStructureModifier)) {
|
if (compile && !(result instanceof CompiledStructureModifier)) {
|
||||||
// Compilation is many orders of magnitude slower than synchronization
|
// Compilation is many orders of magnitude slower than synchronization
|
||||||
synchronized (compiling) {
|
synchronized (compiling) {
|
||||||
final int idCopy = id;
|
|
||||||
final BackgroundCompiler compiler = BackgroundCompiler.getInstance();
|
final BackgroundCompiler compiler = BackgroundCompiler.getInstance();
|
||||||
|
|
||||||
if (!compiling.contains(id) && compiler != null) {
|
if (!compiling.contains(type) && compiler != null) {
|
||||||
compiler.scheduleCompilation(result, new CompileListener<Object>() {
|
compiler.scheduleCompilation(result, new CompileListener<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public void onCompiled(StructureModifier<Object> compiledModifier) {
|
public void onCompiled(StructureModifier<Object> compiledModifier) {
|
||||||
structureModifiers.put(idCopy, compiledModifier);
|
structureModifiers.put(type, compiledModifier);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
compiling.add(id);
|
compiling.add(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
package com.comphenix.protocol.injector.netty;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.ForwardingList;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
// Hopefully, CB won't version these as well
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelFuture;
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelHandler;
|
||||||
|
|
||||||
|
class BootstrapList extends ForwardingList<ChannelFuture> {
|
||||||
|
private List<ChannelFuture> delegate;
|
||||||
|
private ChannelHandler handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new bootstrap list.
|
||||||
|
* @param delegate - the delegate.
|
||||||
|
* @param handler - the channel handler to add.
|
||||||
|
*/
|
||||||
|
public BootstrapList(List<ChannelFuture> delegate, ChannelHandler handler) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.handler = handler;
|
||||||
|
|
||||||
|
// Process all existing bootstraps
|
||||||
|
for (ChannelFuture future : this)
|
||||||
|
processBootstrap(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<ChannelFuture> delegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(ChannelFuture element) {
|
||||||
|
processBootstrap(element);
|
||||||
|
return super.add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends ChannelFuture> collection) {
|
||||||
|
List<? extends ChannelFuture> copy = Lists.newArrayList(collection);
|
||||||
|
|
||||||
|
// Process the collection before we pass it on
|
||||||
|
for (ChannelFuture future : copy) {
|
||||||
|
processBootstrap(future);
|
||||||
|
}
|
||||||
|
return super.addAll(copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelFuture set(int index, ChannelFuture element) {
|
||||||
|
ChannelFuture old = super.set(index, element);
|
||||||
|
|
||||||
|
// Handle the old future, and the newly inserted future
|
||||||
|
if (old != element) {
|
||||||
|
if (old != null) {
|
||||||
|
unprocessBootstrap(old);
|
||||||
|
}
|
||||||
|
if (element != null) {
|
||||||
|
processBootstrap(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a single channel future.
|
||||||
|
* @param future - the future.
|
||||||
|
*/
|
||||||
|
protected void processBootstrap(ChannelFuture future) {
|
||||||
|
future.channel().pipeline().addLast(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revert any changes we made to the channel future.
|
||||||
|
* @param future - the future.
|
||||||
|
*/
|
||||||
|
protected void unprocessBootstrap(ChannelFuture future) {
|
||||||
|
future.channel().pipeline().remove(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close and revert all changes.
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
for (ChannelFuture future : this)
|
||||||
|
unprocessBootstrap(future);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,538 @@
|
|||||||
|
package com.comphenix.protocol.injector.netty;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.util.io.netty.channel.Channel;
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelHandler;
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
||||||
|
import net.minecraft.util.io.netty.channel.socket.SocketChannel;
|
||||||
|
import net.minecraft.util.io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
|
import net.minecraft.util.io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener;
|
||||||
|
import net.sf.cglib.proxy.Factory;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType.Protocol;
|
||||||
|
import com.comphenix.protocol.events.ConnectionSide;
|
||||||
|
import com.comphenix.protocol.events.NetworkMarker;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.events.PacketOutputHandler;
|
||||||
|
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||||
|
import com.comphenix.protocol.injector.server.SocketInjector;
|
||||||
|
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.VolatileField;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection.FieldAccessor;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection.MethodAccessor;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftFields;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.MapMaker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a channel injector.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
class ChannelInjector extends ByteToMessageDecoder {
|
||||||
|
/**
|
||||||
|
* Represents a listener for received or sent packets.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
interface ChannelListener {
|
||||||
|
/**
|
||||||
|
* Invoked when a packet is being sent to the client.
|
||||||
|
* <p>
|
||||||
|
* This is invoked on the main thread.
|
||||||
|
* @param injector - the channel injector.
|
||||||
|
* @param packet - the packet.
|
||||||
|
* @param marker - the associated network marker, if any.
|
||||||
|
* @return The new packet, if it should be changed, or NULL to cancel.
|
||||||
|
*/
|
||||||
|
public Object onPacketSending(ChannelInjector injector, Object packet, NetworkMarker marker);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a packet is being received from a client.
|
||||||
|
* <p>
|
||||||
|
* This is invoked on an asynchronous worker thread.
|
||||||
|
* @param injector - the channel injector.
|
||||||
|
* @param packet - the packet.
|
||||||
|
* @param marker - the associated network marker, if any.
|
||||||
|
* @return The new packet, if it should be changed, or NULL to cancel.
|
||||||
|
*/
|
||||||
|
public Object onPacketReceiving(ChannelInjector injector, Object packet, NetworkMarker marker);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if we need the buffer data of a given client side packet.
|
||||||
|
* @param packetId - the packet Id.
|
||||||
|
* @return TRUE if we do, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean includeBuffer(int packetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ConcurrentMap<Player, ChannelInjector> cachedInjector = new MapMaker().weakKeys().makeMap();
|
||||||
|
|
||||||
|
// The player, or temporary player
|
||||||
|
private Player player;
|
||||||
|
|
||||||
|
// The player connection
|
||||||
|
private Object playerConnection;
|
||||||
|
|
||||||
|
// For retrieving the protocol
|
||||||
|
private FieldAccessor protocolAccessor;
|
||||||
|
|
||||||
|
// The current network manager and channel
|
||||||
|
private final Object networkManager;
|
||||||
|
private final Channel originalChannel;
|
||||||
|
private VolatileField channelField;
|
||||||
|
|
||||||
|
// Known network markers
|
||||||
|
private ConcurrentMap<Object, NetworkMarker> packetMarker = new MapMaker().weakKeys().makeMap();
|
||||||
|
private ConcurrentMap<NetworkMarker, PacketEvent> markerEvent = new MapMaker().weakKeys().makeMap();
|
||||||
|
|
||||||
|
// Packets to ignore
|
||||||
|
private Set<Object> ignoredPackets = Collections.newSetFromMap(new MapMaker().weakKeys().<Object, Boolean>makeMap());
|
||||||
|
|
||||||
|
// Other handlers
|
||||||
|
private ByteToMessageDecoder vanillaDecoder;
|
||||||
|
private MessageToByteEncoder<Object> vanillaEncoder;
|
||||||
|
private MethodAccessor decodeBuffer;
|
||||||
|
private MethodAccessor encodeBuffer;
|
||||||
|
|
||||||
|
// Our extra handler
|
||||||
|
private MessageToByteEncoder<Object> protocolEncoder;
|
||||||
|
|
||||||
|
// The channel listener
|
||||||
|
private ChannelListener channelListener;
|
||||||
|
|
||||||
|
// Closed
|
||||||
|
private boolean injected;
|
||||||
|
private boolean closed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new channel injector.
|
||||||
|
* @param player - the current player, or temporary player.
|
||||||
|
* @param networkManager - its network manager.
|
||||||
|
* @param channel - its channel.
|
||||||
|
*/
|
||||||
|
private ChannelInjector(Player player, Object networkManager, Channel channel, ChannelListener channelListener) {
|
||||||
|
this.player = Preconditions.checkNotNull(player, "player cannot be NULL");
|
||||||
|
this.networkManager = Preconditions.checkNotNull(networkManager, "networkMananger cannot be NULL");
|
||||||
|
this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL");
|
||||||
|
this.channelListener = Preconditions.checkNotNull(channelListener, "channelListener cannot be NULL");
|
||||||
|
|
||||||
|
// Get the channel field
|
||||||
|
this.channelField = new VolatileField(
|
||||||
|
FuzzyReflection.fromObject(networkManager, true).
|
||||||
|
getFieldByType("channel", Channel.class), networkManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct or retrieve a channel injector from an existing Bukkit player.
|
||||||
|
* @param player - the existing Bukkit player.
|
||||||
|
* @return A new injector, or an existing injector associated with this player.
|
||||||
|
*/
|
||||||
|
public static ChannelInjector fromPlayer(Player player, ChannelListener listener) {
|
||||||
|
ChannelInjector injector = cachedInjector.get(player);
|
||||||
|
|
||||||
|
if (injector != null)
|
||||||
|
return injector;
|
||||||
|
|
||||||
|
Object networkManager = MinecraftFields.getNetworkManager(player);
|
||||||
|
Channel channel = FuzzyReflection.getFieldValue(networkManager, Channel.class, true);
|
||||||
|
|
||||||
|
// See if a channel has already been created
|
||||||
|
injector = (ChannelInjector) findChannelHandler(channel, ChannelInjector.class);
|
||||||
|
|
||||||
|
if (injector != null) {
|
||||||
|
// Update the player instance
|
||||||
|
injector.player = player;
|
||||||
|
} else {
|
||||||
|
injector = new ChannelInjector(player, networkManager, channel, listener);
|
||||||
|
}
|
||||||
|
// Cache injector and return
|
||||||
|
cachedInjector.put(player, injector);
|
||||||
|
return injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new channel injector for the given channel.
|
||||||
|
* @param channel - the channel.
|
||||||
|
* @param playerFactory - a temporary player creator.
|
||||||
|
* @return The channel injector.
|
||||||
|
*/
|
||||||
|
public static ChannelInjector fromChannel(Channel channel, ChannelListener listener, TemporaryPlayerFactory playerFactory) {
|
||||||
|
Object networkManager = findNetworkManager(channel);
|
||||||
|
Player temporaryPlayer = playerFactory.createTemporaryPlayer(Bukkit.getServer());
|
||||||
|
ChannelInjector injector = new ChannelInjector(temporaryPlayer, networkManager, channel, listener);
|
||||||
|
|
||||||
|
// Initialize temporary player
|
||||||
|
TemporaryPlayerFactory.setInjectorInPlayer(temporaryPlayer, new ChannelSocketInjector(injector));
|
||||||
|
return injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject the current channel.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public boolean inject() {
|
||||||
|
synchronized (networkManager) {
|
||||||
|
if (originalChannel instanceof Factory)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Don't inject the same channel twice
|
||||||
|
if (findChannelHandler(originalChannel, ChannelInjector.class) != null) {
|
||||||
|
// Invalidate cache
|
||||||
|
if (player != null)
|
||||||
|
cachedInjector.remove(player);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the vanilla decoder, so we don't have to replicate the work
|
||||||
|
vanillaDecoder = (ByteToMessageDecoder) originalChannel.pipeline().get("decoder");
|
||||||
|
vanillaEncoder = (MessageToByteEncoder<Object>) originalChannel.pipeline().get("encoder");
|
||||||
|
decodeBuffer = FuzzyReflection.getMethodAccessor(vanillaDecoder.getClass(),
|
||||||
|
"decode", ChannelHandlerContext.class, ByteBuf.class, List.class);
|
||||||
|
encodeBuffer = FuzzyReflection.getMethodAccessor(vanillaEncoder.getClass(),
|
||||||
|
"encode", ChannelHandlerContext.class, Object.class, ByteBuf.class);
|
||||||
|
|
||||||
|
protocolEncoder = new MessageToByteEncoder<Object>() {
|
||||||
|
@Override
|
||||||
|
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
|
||||||
|
NetworkMarker marker = getMarker(output);
|
||||||
|
PacketEvent event = markerEvent.remove(marker);
|
||||||
|
|
||||||
|
if (event != null && NetworkMarker.hasOutputHandlers(marker)) {
|
||||||
|
ByteBuf packetBuffer = ctx.alloc().buffer();
|
||||||
|
encodeBuffer.invoke(vanillaEncoder, ctx, packet, packetBuffer);
|
||||||
|
byte[] data = getBytes(packetBuffer);
|
||||||
|
|
||||||
|
for (PacketOutputHandler handler : marker.getOutputHandlers()) {
|
||||||
|
handler.handle(event, data);
|
||||||
|
}
|
||||||
|
// Write the result
|
||||||
|
output.writeBytes(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert our handler - note that we replace the decoder with our own
|
||||||
|
originalChannel.pipeline().addBefore("decoder", "protocol_lib_decoder", this);
|
||||||
|
originalChannel.pipeline().addAfter("encoder", "protocol_lib_encoder", protocolEncoder);
|
||||||
|
|
||||||
|
// Intercept all write methods
|
||||||
|
channelField.setValue(new ChannelProxy() {
|
||||||
|
@Override
|
||||||
|
protected Object onMessageWritten(Object message) {
|
||||||
|
return channelListener.onPacketSending(ChannelInjector.this, message, packetMarker.get(message));
|
||||||
|
}
|
||||||
|
}.asChannel(originalChannel));
|
||||||
|
|
||||||
|
injected = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the current injector.
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
if (!closed) {
|
||||||
|
closed = true;
|
||||||
|
|
||||||
|
if (injected) {
|
||||||
|
originalChannel.pipeline().remove(this);
|
||||||
|
originalChannel.pipeline().remove(protocolEncoder);
|
||||||
|
channelField.revertValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List<Object> packets) throws Exception {
|
||||||
|
byteBuffer.markReaderIndex();
|
||||||
|
decodeBuffer.invoke(vanillaDecoder, ctx, byteBuffer, packets);
|
||||||
|
|
||||||
|
if (packets.size() > 0) {
|
||||||
|
Object input = packets.get(0);
|
||||||
|
int id = PacketRegistry.getPacketID(input.getClass());
|
||||||
|
NetworkMarker marker = null;
|
||||||
|
|
||||||
|
if (channelListener.includeBuffer(id)) {
|
||||||
|
byteBuffer.resetReaderIndex();
|
||||||
|
marker = new NetworkMarker(ConnectionSide.CLIENT_SIDE, getBytes(byteBuffer));
|
||||||
|
}
|
||||||
|
Object output = channelListener.onPacketReceiving(this, input, marker);
|
||||||
|
|
||||||
|
// Handle packet changes
|
||||||
|
if (output == null)
|
||||||
|
packets.clear();
|
||||||
|
else if (output != input)
|
||||||
|
packets.set(0, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve every byte in the given byte buffer.
|
||||||
|
* @param buffer - the buffer.
|
||||||
|
* @return The bytes.
|
||||||
|
*/
|
||||||
|
private byte[] getBytes(ByteBuf buffer){
|
||||||
|
byte[] data = new byte[buffer.readableBytes()];
|
||||||
|
|
||||||
|
buffer.readBytes(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect the current player.
|
||||||
|
* @param message - the disconnect message, if possible.
|
||||||
|
*/
|
||||||
|
private void disconnect(String message) {
|
||||||
|
// If we're logging in, we can only close the channel
|
||||||
|
if (playerConnection == null || player instanceof Factory) {
|
||||||
|
originalChannel.disconnect();
|
||||||
|
} else {
|
||||||
|
// Call the disconnect method
|
||||||
|
try {
|
||||||
|
MinecraftMethods.getDisconnectMethod(playerConnection.getClass()).
|
||||||
|
invoke(playerConnection, message);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("Unable to invoke disconnect method.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a packet to a player's client.
|
||||||
|
* @param packet - the packet to send.
|
||||||
|
* @param marker - the network marker.
|
||||||
|
* @param filtered - whether or not the packet is filtered.
|
||||||
|
*/
|
||||||
|
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||||
|
saveMarker(packet, marker);
|
||||||
|
|
||||||
|
// Record if this packet should be ignored by most listeners
|
||||||
|
if (!filtered) {
|
||||||
|
ignoredPackets.add(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to send the packet with NetworkMarker.handle(), or the PlayerConnection if its active
|
||||||
|
try {
|
||||||
|
if (player instanceof Factory) {
|
||||||
|
MinecraftMethods.getNetworkManagerHandleMethod().invoke(networkManager, packet, new GenericFutureListener[0]);
|
||||||
|
} else {
|
||||||
|
MinecraftMethods.getSendPacketMethod().invoke(getPlayerConnection(), packet);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Unable to send server packet " + packet, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recieve a packet on the server.
|
||||||
|
* @param packet - the (NMS) packet to send.
|
||||||
|
* @param marker - the network marker.
|
||||||
|
* @param filtered - whether or not the packet is filtered.
|
||||||
|
*/
|
||||||
|
public void recieveClientPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||||
|
saveMarker(packet, marker);
|
||||||
|
|
||||||
|
if (!filtered) {
|
||||||
|
ignoredPackets.add(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
MinecraftMethods.getNetworkManagerReadPacketMethod().invoke(networkManager, null, packet);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("Unable to recieve client packet " + packet, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the current protocol state.
|
||||||
|
* @return The current protocol.
|
||||||
|
*/
|
||||||
|
public Protocol getCurrentProtocol() {
|
||||||
|
if (protocolAccessor == null) {
|
||||||
|
protocolAccessor = FuzzyReflection.getFieldAccessor(
|
||||||
|
networkManager.getClass(), MinecraftReflection.getEnumProtocolClass(), true);
|
||||||
|
}
|
||||||
|
return Protocol.fromVanilla((Enum<?>) protocolAccessor.get(networkManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the player connection of the current player.
|
||||||
|
* @return The player connection.
|
||||||
|
*/
|
||||||
|
private Object getPlayerConnection() {
|
||||||
|
if (playerConnection == null) {
|
||||||
|
playerConnection = MinecraftFields.getPlayerConnection(player);
|
||||||
|
}
|
||||||
|
return playerConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo the ignore status of a packet.
|
||||||
|
* @param packet - the packet.
|
||||||
|
* @return TRUE if the ignore status was undone, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean unignorePacket(Object packet) {
|
||||||
|
return ignoredPackets.remove(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ignore the given packet.
|
||||||
|
* @param packet - the packet to ignore.
|
||||||
|
* @return TRUE if it was ignored, FALSE if it already is ignored.
|
||||||
|
*/
|
||||||
|
public boolean ignorePacket(Object packet) {
|
||||||
|
return ignoredPackets.add(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the network marker associated with a given packet.
|
||||||
|
* @param packet - the packet.
|
||||||
|
* @return The network marker.
|
||||||
|
*/
|
||||||
|
public NetworkMarker getMarker(Object packet) {
|
||||||
|
return packetMarker.get(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate a given network marker with a specific packet.
|
||||||
|
* @param packet - the NMS packet.
|
||||||
|
* @param marker - the associated marker.
|
||||||
|
*/
|
||||||
|
public void saveMarker(Object packet, NetworkMarker marker) {
|
||||||
|
if (marker != null) {
|
||||||
|
packetMarker.put(packet, marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate a given network marker with a packet event.
|
||||||
|
* @param marker - the marker.
|
||||||
|
* @param event - the packet event
|
||||||
|
*/
|
||||||
|
public void saveEvent(NetworkMarker marker, PacketEvent event) {
|
||||||
|
if (marker != null) {
|
||||||
|
markerEvent.put(marker, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the network manager in a channel's pipeline.
|
||||||
|
* @param channel - the channel.
|
||||||
|
* @return The network manager.
|
||||||
|
*/
|
||||||
|
private static Object findNetworkManager(Channel channel) {
|
||||||
|
// Find the network manager
|
||||||
|
Object networkManager = findChannelHandler(channel, MinecraftReflection.getNetworkManagerClass());
|
||||||
|
|
||||||
|
if (networkManager != null)
|
||||||
|
return networkManager;
|
||||||
|
throw new IllegalArgumentException("Unable to find NetworkManager in " + channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the first channel handler that is assignable to a given type.
|
||||||
|
* @param channel - the channel.
|
||||||
|
* @param clazz - the type.
|
||||||
|
* @return The first handler, or NULL.
|
||||||
|
*/
|
||||||
|
private static ChannelHandler findChannelHandler(Channel channel, Class<?> clazz) {
|
||||||
|
for (Entry<String, ChannelHandler> entry : channel.pipeline()) {
|
||||||
|
if (clazz.isAssignableFrom(entry.getValue().getClass())) {
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the current player or temporary player associated with the injector.
|
||||||
|
* @return The current player.
|
||||||
|
*/
|
||||||
|
public Player getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the channel has already been injected.
|
||||||
|
* @return TRUE if it has, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isInjected() {
|
||||||
|
return injected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this channel has been closed and cleaned up.
|
||||||
|
* @return TRUE if it has, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isClosed() {
|
||||||
|
return closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a socket injector that foreards to the current channel injector.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
private static class ChannelSocketInjector implements SocketInjector {
|
||||||
|
private final ChannelInjector injector;
|
||||||
|
|
||||||
|
public ChannelSocketInjector(ChannelInjector injector) {
|
||||||
|
this.injector = injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket getSocket() throws IllegalAccessException {
|
||||||
|
return NettySocketAdaptor.adapt((SocketChannel) injector.originalChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SocketAddress getAddress() throws IllegalAccessException {
|
||||||
|
return injector.originalChannel.localAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect(String message) throws InvocationTargetException {
|
||||||
|
injector.disconnect(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException {
|
||||||
|
injector.sendServerPacket(packet, marker, filtered);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Player getPlayer() {
|
||||||
|
return injector.player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Player getUpdatedPlayer() {
|
||||||
|
return injector.player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferState(SocketInjector delegate) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUpdatedPlayer(Player updatedPlayer) {
|
||||||
|
injector.player = updatedPlayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.comphenix.protocol.injector.netty;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
|
|
||||||
|
import net.minecraft.util.com.google.common.collect.Sets;
|
||||||
|
import net.minecraft.util.io.netty.channel.Channel;
|
||||||
|
import net.sf.cglib.proxy.Enhancer;
|
||||||
|
import net.sf.cglib.proxy.MethodInterceptor;
|
||||||
|
import net.sf.cglib.proxy.MethodProxy;
|
||||||
|
|
||||||
|
abstract class ChannelProxy {
|
||||||
|
private static Set<Method> WRITE_METHODS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the channel proxy object.
|
||||||
|
* @param proxyInstance - the proxy instance object.
|
||||||
|
* @return The channel proxy.
|
||||||
|
*/
|
||||||
|
public Channel asChannel(final Channel proxyInstance) {
|
||||||
|
// Simple way to match all the write methods
|
||||||
|
if (WRITE_METHODS == null) {
|
||||||
|
List<Method> writers = FuzzyReflection.fromClass(Channel.class).
|
||||||
|
getMethodList(FuzzyMethodContract.newBuilder().nameRegex("write.*").build());
|
||||||
|
WRITE_METHODS = Sets.newHashSet(writers);
|
||||||
|
}
|
||||||
|
|
||||||
|
Enhancer enhancer = new Enhancer();
|
||||||
|
enhancer.setSuperclass(Channel.class);
|
||||||
|
enhancer.setCallback(new MethodInterceptor() {
|
||||||
|
@Override
|
||||||
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||||
|
if (WRITE_METHODS.contains(method)) {
|
||||||
|
args[0] = onMessageWritten(args[0]);
|
||||||
|
|
||||||
|
// If we should skip this object
|
||||||
|
if (args[0] == null)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Forward to proxy
|
||||||
|
return proxy.invoke(proxyInstance, args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return (Channel) enhancer.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a packet is being transmitted.
|
||||||
|
* @param message - the packet to transmit.
|
||||||
|
* @return The object to transmit.
|
||||||
|
*/
|
||||||
|
protected abstract Object onMessageWritten(Object message);
|
||||||
|
}
|
@ -1,28 +0,0 @@
|
|||||||
package com.comphenix.protocol.injector.netty;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a way of accessing the new netty Protocol enum.
|
|
||||||
* @author Kristian
|
|
||||||
*/
|
|
||||||
public class NettyProtocol {
|
|
||||||
private Class<?> enumProtocol;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public NettyProtocol() {
|
|
||||||
enumProtocol = MinecraftReflection.getEnumProtocolClass();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the packet lookup tables in each protocol.
|
|
||||||
*/
|
|
||||||
private void initialize() {
|
|
||||||
for (Object protocol : enumProtocol.getEnumConstants()) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,309 @@
|
|||||||
|
package com.comphenix.protocol.injector.netty;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import net.minecraft.util.io.netty.channel.Channel;
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelFuture;
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelHandler;
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelInboundHandler;
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelInitializer;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.Packets;
|
||||||
|
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||||
|
import com.comphenix.protocol.events.ConnectionSide;
|
||||||
|
import com.comphenix.protocol.events.NetworkMarker;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
|
import com.comphenix.protocol.injector.netty.ChannelInjector.ChannelListener;
|
||||||
|
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||||
|
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||||
|
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||||
|
import com.comphenix.protocol.injector.spigot.AbstractPacketInjector;
|
||||||
|
import com.comphenix.protocol.injector.spigot.AbstractPlayerHandler;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.VolatileField;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
|
||||||
|
public class NettyProtocolInjector implements ChannelListener {
|
||||||
|
private volatile boolean injected;
|
||||||
|
private volatile boolean closed;
|
||||||
|
|
||||||
|
// The temporary player factory
|
||||||
|
private TemporaryPlayerFactory playerFactory = new TemporaryPlayerFactory();
|
||||||
|
private VolatileField bootstrapField;
|
||||||
|
|
||||||
|
// Different sending filters
|
||||||
|
private IntegerSet queuedFilters = new IntegerSet(Packets.PACKET_COUNT);
|
||||||
|
private IntegerSet reveivedFilters = new IntegerSet(Packets.PACKET_COUNT);
|
||||||
|
|
||||||
|
// Which packets are buffered
|
||||||
|
private Set<Integer> bufferedPackets;
|
||||||
|
|
||||||
|
private ListenerInvoker invoker;
|
||||||
|
|
||||||
|
public NettyProtocolInjector(ListenerInvoker invoker) {
|
||||||
|
this.invoker = invoker;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject into the spigot connection class.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public synchronized void inject() {
|
||||||
|
if (injected)
|
||||||
|
throw new IllegalStateException("Cannot inject twice.");
|
||||||
|
try {
|
||||||
|
FuzzyReflection fuzzyServer = FuzzyReflection.fromClass(MinecraftReflection.getMinecraftServerClass());
|
||||||
|
Method serverConnectionMethod = fuzzyServer.getMethodByParameters("getServerConnection", MinecraftReflection.getServerConnectionClass(), new Class[] {});
|
||||||
|
|
||||||
|
// Get the server connection
|
||||||
|
Object server = fuzzyServer.getSingleton();
|
||||||
|
Object serverConnection = serverConnectionMethod.invoke(server);
|
||||||
|
|
||||||
|
// Handle connected channels
|
||||||
|
final ChannelInboundHandler initProtocol = new ChannelInitializer<Channel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel channel) throws Exception {
|
||||||
|
// Check and see if the injector has closed
|
||||||
|
synchronized (this) {
|
||||||
|
if (closed)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ChannelInjector.fromChannel(channel, NettyProtocolInjector.this, playerFactory).inject();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add our handler to newly created channels
|
||||||
|
final ChannelHandler connectionHandler = new ChannelInboundHandlerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
|
Channel channel = (Channel) msg;
|
||||||
|
|
||||||
|
// Execute the other handlers before adding our own
|
||||||
|
ctx.fireChannelRead(msg);
|
||||||
|
channel.pipeline().addLast(initProtocol);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert ProtocolLib's connection interceptor
|
||||||
|
bootstrapField = getBootstrapField(serverConnection);
|
||||||
|
bootstrapField.setValue(new BootstrapList(
|
||||||
|
(List<ChannelFuture>) bootstrapField.getValue(), connectionHandler
|
||||||
|
));
|
||||||
|
|
||||||
|
injected = true;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Unable to inject channel futures.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject our packet handling into a specific player.
|
||||||
|
* @param player
|
||||||
|
*/
|
||||||
|
public void injectPlayer(Player player) {
|
||||||
|
ChannelInjector.fromPlayer(player, this).inject();
|
||||||
|
}
|
||||||
|
|
||||||
|
private VolatileField getBootstrapField(Object serverConnection) {
|
||||||
|
VolatileField firstVolatile = null;
|
||||||
|
|
||||||
|
for (Field field : FuzzyReflection.fromObject(serverConnection, true).getFieldListByType(List.class)) {
|
||||||
|
VolatileField currentVolatile = new VolatileField(field, serverConnection, true);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object> list = (List<Object>) currentVolatile.getValue();
|
||||||
|
|
||||||
|
// Also save the first list
|
||||||
|
if (firstVolatile == null) {
|
||||||
|
firstVolatile = currentVolatile;
|
||||||
|
}
|
||||||
|
if (list.size() > 0 && list.get(0) instanceof ChannelFuture) {
|
||||||
|
return currentVolatile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstVolatile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up any remaning injections.
|
||||||
|
*/
|
||||||
|
public synchronized void close() {
|
||||||
|
if (!closed) {
|
||||||
|
closed = true;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object> bootstraps = (List<Object>) bootstrapField.getValue();
|
||||||
|
|
||||||
|
// Remember to close all the bootstraps
|
||||||
|
for (Object value : bootstraps) {
|
||||||
|
if (value instanceof BootstrapList) {
|
||||||
|
((BootstrapList) value).close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uninject all the players
|
||||||
|
for (Player player : Bukkit.getServer().getOnlinePlayers()) {
|
||||||
|
ChannelInjector.fromPlayer(player, this).close();
|
||||||
|
}
|
||||||
|
bootstrapField.revertValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object onPacketSending(ChannelInjector injector, Object packet, NetworkMarker marker) {
|
||||||
|
Integer id = invoker.getPacketID(packet);
|
||||||
|
|
||||||
|
if (id != null && queuedFilters.contains(id)) {
|
||||||
|
// Check for ignored packets
|
||||||
|
if (injector.unignorePacket(packet)) {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
PacketContainer container = new PacketContainer(id, packet);
|
||||||
|
PacketEvent event = packetQueued(container, injector.getPlayer());
|
||||||
|
|
||||||
|
if (!event.isCancelled()) {
|
||||||
|
injector.saveEvent(marker, event);
|
||||||
|
return event.getPacket().getHandle();
|
||||||
|
} else {
|
||||||
|
return null; // Cancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Don't change anything
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object onPacketReceiving(ChannelInjector injector, Object packet, NetworkMarker marker) {
|
||||||
|
Integer id = invoker.getPacketID(packet);
|
||||||
|
|
||||||
|
if (id != null && reveivedFilters.contains(id)) {
|
||||||
|
// Check for ignored packets
|
||||||
|
if (injector.unignorePacket(packet)) {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
PacketContainer container = new PacketContainer(id, packet);
|
||||||
|
PacketEvent event = packetReceived(container, injector.getPlayer(), marker);
|
||||||
|
|
||||||
|
if (!event.isCancelled()) {
|
||||||
|
return event.getPacket().getHandle();
|
||||||
|
} else {
|
||||||
|
return null; // Cancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Don't change anything
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean includeBuffer(int packetId) {
|
||||||
|
return bufferedPackets.contains(packetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to inform the event listeners of a queued packet.
|
||||||
|
* @param packet - the packet that is to be sent.
|
||||||
|
* @param reciever - the reciever of this packet.
|
||||||
|
* @return The packet event that was used.
|
||||||
|
*/
|
||||||
|
private PacketEvent packetQueued(PacketContainer packet, Player reciever) {
|
||||||
|
PacketEvent event = PacketEvent.fromServer(this, packet, reciever);
|
||||||
|
|
||||||
|
invoker.invokePacketSending(event);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to inform the event listeners of a received packet.
|
||||||
|
* @param packet - the packet that has been receieved.
|
||||||
|
* @param sender - the client packet.
|
||||||
|
* @param marker - the network marker.
|
||||||
|
* @return The packet event that was used.
|
||||||
|
*/
|
||||||
|
private PacketEvent packetReceived(PacketContainer packet, Player sender, NetworkMarker marker) {
|
||||||
|
PacketEvent event = PacketEvent.fromClient(this, packet, marker, sender);
|
||||||
|
|
||||||
|
invoker.invokePacketRecieving(event);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerInjectionHandler getPlayerInjector() {
|
||||||
|
return new AbstractPlayerHandler(queuedFilters) {
|
||||||
|
private ChannelListener listener = NettyProtocolInjector.this;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updatePlayer(Player player) {
|
||||||
|
// Ignore it
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean uninjectPlayer(InetSocketAddress address) {
|
||||||
|
// Ignore this too
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean uninjectPlayer(Player player) {
|
||||||
|
ChannelInjector.fromPlayer(player, listener).close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendServerPacket(Player reciever, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
|
||||||
|
ChannelInjector.fromPlayer(reciever, listener).sendServerPacket(packet.getHandle(), marker, filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recieveClientPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
|
||||||
|
ChannelInjector.fromPlayer(player, listener).recieveClientPacket(mcPacket, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectPlayer(Player player, ConflictStrategy strategy) {
|
||||||
|
ChannelInjector.fromPlayer(player, listener).inject();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketEvent handlePacketRecieved(PacketContainer packet, InputStream input, byte[] buffered) {
|
||||||
|
// Ignore this
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleDisconnect(Player player) {
|
||||||
|
ChannelInjector.fromPlayer(player, listener).close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a view of this protocol injector as a packet injector.
|
||||||
|
* @return The packet injector.
|
||||||
|
*/
|
||||||
|
public PacketInjector getPacketInjector() {
|
||||||
|
return new AbstractPacketInjector(reveivedFilters) {
|
||||||
|
@Override
|
||||||
|
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
|
||||||
|
NetworkMarker marker = buffered != null ? new NetworkMarker(ConnectionSide.CLIENT_SIDE, buffered) : null;
|
||||||
|
ChannelInjector.fromPlayer(client, NettyProtocolInjector.this).saveMarker(packet.getHandle(), marker);
|
||||||
|
return packetReceived(packet, client, marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void inputBuffersChanged(Set<Integer> set) {
|
||||||
|
bufferedPackets = set;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
package com.comphenix.protocol.injector.netty;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
|
import com.comphenix.protocol.PacketType.Protocol;
|
||||||
|
import com.comphenix.protocol.PacketType.Sender;
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.google.common.collect.BiMap;
|
||||||
|
import com.google.common.collect.HashBiMap;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a way of accessing the new netty Protocol enum.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
// TODO: Handle modifications to the BiMap
|
||||||
|
public class NettyProtocolRegistry {
|
||||||
|
private Class<?> enumProtocol;
|
||||||
|
|
||||||
|
// The main lookup table
|
||||||
|
private BiMap<PacketType, Class<?>> typeToClass = HashBiMap.create();
|
||||||
|
private Set<PacketType> serverPackets = Sets.newHashSet();
|
||||||
|
private Set<PacketType> clientPackets = Sets.newHashSet();
|
||||||
|
|
||||||
|
public NettyProtocolRegistry() {
|
||||||
|
enumProtocol = MinecraftReflection.getEnumProtocolClass();
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an immutable view of the packet type lookup.
|
||||||
|
* @return The packet type lookup.
|
||||||
|
*/
|
||||||
|
public Map<PacketType, Class<?>> getPacketTypeLookup() {
|
||||||
|
return Collections.unmodifiableMap(typeToClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an immutable view of the class to packet tyåe lookup.
|
||||||
|
* @return The packet type lookup.
|
||||||
|
*/
|
||||||
|
public Map<Class<?>, PacketType> getPacketClassLookup() {
|
||||||
|
return Collections.unmodifiableMap(typeToClass.inverse());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve every known client packet, from every protocol.
|
||||||
|
* @return Every client packet.
|
||||||
|
*/
|
||||||
|
public Set<PacketType> getClientPackets() {
|
||||||
|
return Collections.unmodifiableSet(clientPackets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve every known server packet, from every protocol.
|
||||||
|
* @return Every server packet.
|
||||||
|
*/
|
||||||
|
public Set<PacketType> getServerPackets() {
|
||||||
|
return Collections.unmodifiableSet(serverPackets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the packet lookup tables in each protocol.
|
||||||
|
*/
|
||||||
|
private void initialize() {
|
||||||
|
final Object[] protocols = enumProtocol.getEnumConstants();
|
||||||
|
List<Map<Integer, Class<?>>> serverPackets = Lists.newArrayList();
|
||||||
|
List<Map<Integer, Class<?>>> clientPackets = Lists.newArrayList();
|
||||||
|
StructureModifier<Object> modifier = null;
|
||||||
|
|
||||||
|
for (Object protocol : protocols) {
|
||||||
|
if (modifier == null)
|
||||||
|
modifier = new StructureModifier<Object>(protocol.getClass().getSuperclass(), false);
|
||||||
|
StructureModifier<Map<Integer, Class<?>>> maps = modifier.withTarget(protocol).withType(Map.class);
|
||||||
|
|
||||||
|
serverPackets.add(maps.read(0));
|
||||||
|
clientPackets.add(maps.read(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heuristic - there are more server packets than client packets
|
||||||
|
if (sum(clientPackets) > sum(serverPackets)) {
|
||||||
|
// Swap if this is violated
|
||||||
|
List<Map<Integer, Class<?>>> temp = serverPackets;
|
||||||
|
serverPackets = clientPackets;
|
||||||
|
clientPackets = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < protocols.length; i++) {
|
||||||
|
Enum<?> enumProtocol = (Enum<?>) protocols[i];
|
||||||
|
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
|
||||||
|
|
||||||
|
// Associate known types
|
||||||
|
associatePackets(serverPackets.get(i), equivalent, Sender.SERVER);
|
||||||
|
associatePackets(clientPackets.get(i), equivalent, Sender.CLIENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void associatePackets(Map<Integer, Class<?>> lookup, Protocol protocol, Sender sender) {
|
||||||
|
for (Entry<Integer, Class<?>> entry : lookup.entrySet()) {
|
||||||
|
PacketType type = PacketType.fromCurrent(protocol, sender, entry.getKey(), PacketType.UNKNOWN_PACKET);
|
||||||
|
typeToClass.put(type, entry.getValue());
|
||||||
|
|
||||||
|
if (sender == Sender.SERVER)
|
||||||
|
serverPackets.add(type);
|
||||||
|
if (sender == Sender.CLIENT)
|
||||||
|
clientPackets.add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the number of mapping in all the maps.
|
||||||
|
* @param maps - iterable of maps.
|
||||||
|
* @return The sum of all the entries.
|
||||||
|
*/
|
||||||
|
private int sum(Iterable<? extends Map<Integer, Class<?>>> maps) {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (Map<Integer, Class<?>> map : maps)
|
||||||
|
count += map.size();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,248 @@
|
|||||||
|
package com.comphenix.protocol.injector.netty;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.net.SocketException;
|
||||||
|
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelOption;
|
||||||
|
import net.minecraft.util.io.netty.channel.socket.SocketChannel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class wraps a Netty {@link Channel} in a {@link Socket}. It overrides
|
||||||
|
* all methods in {@link Socket} to ensure that calls are not mistakingly made
|
||||||
|
* to the unsupported super socket. All operations that can be sanely applied to
|
||||||
|
* a {@link Channel} are implemented here. Those which cannot will throw an
|
||||||
|
* {@link UnsupportedOperationException}.
|
||||||
|
*/
|
||||||
|
// Thanks MD5. :)
|
||||||
|
class NettySocketAdaptor extends Socket {
|
||||||
|
private final SocketChannel ch;
|
||||||
|
|
||||||
|
private NettySocketAdaptor(SocketChannel ch) {
|
||||||
|
this.ch = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NettySocketAdaptor adapt(SocketChannel ch) {
|
||||||
|
return new NettySocketAdaptor(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bind(SocketAddress bindpoint) throws IOException {
|
||||||
|
ch.bind(bindpoint).syncUninterruptibly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void close() throws IOException {
|
||||||
|
ch.close().syncUninterruptibly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect(SocketAddress endpoint) throws IOException {
|
||||||
|
ch.connect(endpoint).syncUninterruptibly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect(SocketAddress endpoint, int timeout) throws IOException {
|
||||||
|
ch.config().setConnectTimeoutMillis(timeout);
|
||||||
|
ch.connect(endpoint).syncUninterruptibly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj instanceof NettySocketAdaptor && ch.equals(((NettySocketAdaptor) obj).ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.nio.channels.SocketChannel getChannel() {
|
||||||
|
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InetAddress getInetAddress() {
|
||||||
|
return ch.remoteAddress().getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getKeepAlive() throws SocketException {
|
||||||
|
return ch.config().getOption(ChannelOption.SO_KEEPALIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InetAddress getLocalAddress() {
|
||||||
|
return ch.localAddress().getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLocalPort() {
|
||||||
|
return ch.localAddress().getPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SocketAddress getLocalSocketAddress() {
|
||||||
|
return ch.localAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getOOBInline() throws SocketException {
|
||||||
|
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream getOutputStream() throws IOException {
|
||||||
|
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPort() {
|
||||||
|
return ch.remoteAddress().getPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int getReceiveBufferSize() throws SocketException {
|
||||||
|
return ch.config().getOption(ChannelOption.SO_RCVBUF);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SocketAddress getRemoteSocketAddress() {
|
||||||
|
return ch.remoteAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getReuseAddress() throws SocketException {
|
||||||
|
return ch.config().getOption(ChannelOption.SO_REUSEADDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int getSendBufferSize() throws SocketException {
|
||||||
|
return ch.config().getOption(ChannelOption.SO_SNDBUF);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSoLinger() throws SocketException {
|
||||||
|
return ch.config().getOption(ChannelOption.SO_LINGER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int getSoTimeout() throws SocketException {
|
||||||
|
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getTcpNoDelay() throws SocketException {
|
||||||
|
return ch.config().getOption(ChannelOption.TCP_NODELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTrafficClass() throws SocketException {
|
||||||
|
return ch.config().getOption(ChannelOption.IP_TOS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return ch.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBound() {
|
||||||
|
return ch.localAddress() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClosed() {
|
||||||
|
return !ch.isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnected() {
|
||||||
|
return ch.isActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInputShutdown() {
|
||||||
|
return ch.isInputShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOutputShutdown() {
|
||||||
|
return ch.isOutputShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendUrgentData(int data) throws IOException {
|
||||||
|
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setKeepAlive(boolean on) throws SocketException {
|
||||||
|
ch.config().setOption(ChannelOption.SO_KEEPALIVE, on);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOOBInline(boolean on) throws SocketException {
|
||||||
|
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
|
||||||
|
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void setReceiveBufferSize(int size) throws SocketException {
|
||||||
|
ch.config().setOption(ChannelOption.SO_RCVBUF, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReuseAddress(boolean on) throws SocketException {
|
||||||
|
ch.config().setOption(ChannelOption.SO_REUSEADDR, on);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void setSendBufferSize(int size) throws SocketException {
|
||||||
|
ch.config().setOption(ChannelOption.SO_SNDBUF, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSoLinger(boolean on, int linger) throws SocketException {
|
||||||
|
ch.config().setOption(ChannelOption.SO_LINGER, linger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void setSoTimeout(int timeout) throws SocketException {
|
||||||
|
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTcpNoDelay(boolean on) throws SocketException {
|
||||||
|
ch.config().setOption(ChannelOption.TCP_NODELAY, on);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTrafficClass(int tc) throws SocketException {
|
||||||
|
ch.config().setOption(ChannelOption.IP_TOS, tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdownInput() throws IOException {
|
||||||
|
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdownOutput() throws IOException {
|
||||||
|
ch.shutdownOutput().syncUninterruptibly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return ch.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,344 @@
|
|||||||
|
package com.comphenix.protocol.injector.packet;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.sf.cglib.proxy.Factory;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.comphenix.protocol.wrappers.TroveWrapper;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
class LegacyPacketRegistry {
|
||||||
|
private static final int MIN_SERVER_PACKETS = 5;
|
||||||
|
private static final int MIN_CLIENT_PACKETS = 5;
|
||||||
|
|
||||||
|
// Fuzzy reflection
|
||||||
|
private FuzzyReflection packetRegistry;
|
||||||
|
|
||||||
|
// The packet class to packet ID translator
|
||||||
|
private Map<Class, Integer> packetToID;
|
||||||
|
|
||||||
|
// Packet IDs to classes, grouped by whether or not they're vanilla or custom defined
|
||||||
|
private Multimap<Integer, Class> customIdToPacket;
|
||||||
|
private Map<Integer, Class> vanillaIdToPacket;
|
||||||
|
|
||||||
|
// Whether or not certain packets are sent by the client or the server
|
||||||
|
private ImmutableSet<Integer> serverPackets;
|
||||||
|
private ImmutableSet<Integer> clientPackets;
|
||||||
|
|
||||||
|
// The underlying sets
|
||||||
|
private Set<Integer> serverPacketsRef;
|
||||||
|
private Set<Integer> clientPacketsRef;
|
||||||
|
|
||||||
|
// New proxy values
|
||||||
|
private Map<Integer, Class> overwrittenPackets = new HashMap<Integer, Class>();
|
||||||
|
|
||||||
|
// Vanilla packets
|
||||||
|
private Map<Integer, Class> previousValues = new HashMap<Integer, Class>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the registry.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({ "unchecked" })
|
||||||
|
public void initialize() {
|
||||||
|
if (packetToID == null) {
|
||||||
|
try {
|
||||||
|
Field packetsField = getPacketRegistry().getFieldByType("packetsField", Map.class);
|
||||||
|
packetToID = (Map<Class, Integer>) FieldUtils.readStaticField(packetsField, true);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Spigot 1.2.5 MCPC workaround
|
||||||
|
try {
|
||||||
|
packetToID = getSpigotWrapper();
|
||||||
|
} catch (Exception e2) {
|
||||||
|
// Very bad indeed
|
||||||
|
throw new IllegalArgumentException(e.getMessage() + "; Spigot workaround failed.", e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Unable to retrieve the packetClassToIdMap", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the inverse maps
|
||||||
|
customIdToPacket = InverseMaps.inverseMultimap(packetToID, new Predicate<Map.Entry<Class, Integer>>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(@Nullable Entry<Class, Integer> entry) {
|
||||||
|
return !MinecraftReflection.isMinecraftClass(entry.getKey());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// And the vanilla pack - here we assume a unique ID to class mapping
|
||||||
|
vanillaIdToPacket = InverseMaps.inverseMap(packetToID, new Predicate<Map.Entry<Class, Integer>>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(@Nullable Entry<Class, Integer> entry) {
|
||||||
|
return MinecraftReflection.isMinecraftClass(entry.getKey());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
initializeSets();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void initializeSets() throws FieldAccessException {
|
||||||
|
if (serverPacketsRef == null || clientPacketsRef == null) {
|
||||||
|
List<Field> sets = getPacketRegistry().getFieldListByType(Set.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (sets.size() > 1) {
|
||||||
|
serverPacketsRef = (Set<Integer>) FieldUtils.readStaticField(sets.get(0), true);
|
||||||
|
clientPacketsRef = (Set<Integer>) FieldUtils.readStaticField(sets.get(1), true);
|
||||||
|
|
||||||
|
// Impossible
|
||||||
|
if (serverPacketsRef == null || clientPacketsRef == null)
|
||||||
|
throw new FieldAccessException("Packet sets are in an illegal state.");
|
||||||
|
|
||||||
|
// NEVER allow callers to modify the underlying sets
|
||||||
|
serverPackets = ImmutableSet.copyOf(serverPacketsRef);
|
||||||
|
clientPackets = ImmutableSet.copyOf(clientPacketsRef);
|
||||||
|
|
||||||
|
// Check sizes
|
||||||
|
if (serverPackets.size() < MIN_SERVER_PACKETS)
|
||||||
|
throw new InsufficientPacketsException("Insufficient server packets.", false, serverPackets.size());
|
||||||
|
if (clientPackets.size() < MIN_CLIENT_PACKETS)
|
||||||
|
throw new InsufficientPacketsException("Insufficient client packets.", true, clientPackets.size());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new FieldAccessException("Cannot retrieve packet client/server sets.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new FieldAccessException("Cannot access field.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Copy over again if it has changed
|
||||||
|
if (serverPacketsRef != null && serverPacketsRef.size() != serverPackets.size())
|
||||||
|
serverPackets = ImmutableSet.copyOf(serverPacketsRef);
|
||||||
|
if (clientPacketsRef != null && clientPacketsRef.size() != clientPackets.size())
|
||||||
|
clientPackets = ImmutableSet.copyOf(clientPacketsRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the packet mapping.
|
||||||
|
* @return The packet map.
|
||||||
|
*/
|
||||||
|
public Map<Class, Integer> getPacketToID() {
|
||||||
|
// Initialize it, if we haven't already
|
||||||
|
if (packetToID == null) {
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
return packetToID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Class, Integer> getSpigotWrapper() throws IllegalAccessException {
|
||||||
|
// If it talks like a duck, etc.
|
||||||
|
// Perhaps it would be nice to have a proper duck typing library as well
|
||||||
|
FuzzyClassContract mapLike = FuzzyClassContract.newBuilder().
|
||||||
|
method(FuzzyMethodContract.newBuilder().
|
||||||
|
nameExact("size").returnTypeExact(int.class)).
|
||||||
|
method(FuzzyMethodContract.newBuilder().
|
||||||
|
nameExact("put").parameterCount(2)).
|
||||||
|
method(FuzzyMethodContract.newBuilder().
|
||||||
|
nameExact("get").parameterCount(1)).
|
||||||
|
build();
|
||||||
|
|
||||||
|
Field packetsField = getPacketRegistry().getField(
|
||||||
|
FuzzyFieldContract.newBuilder().typeMatches(mapLike).build());
|
||||||
|
Object troveMap = FieldUtils.readStaticField(packetsField, true);
|
||||||
|
|
||||||
|
// Check for stupid no_entry_values
|
||||||
|
try {
|
||||||
|
Field field = FieldUtils.getField(troveMap.getClass(), "no_entry_value", true);
|
||||||
|
Integer value = (Integer) FieldUtils.readField(field, troveMap, true);
|
||||||
|
|
||||||
|
if (value >= 0 && value < 256) {
|
||||||
|
// Someone forgot to set the no entry value. Let's help them.
|
||||||
|
FieldUtils.writeField(field, troveMap, -1);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new CannotCorrectTroveMapException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll assume this a Trove map
|
||||||
|
return TroveWrapper.getDecoratedMap(troveMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the cached fuzzy reflection instance allowing access to the packet registry.
|
||||||
|
* @return Reflected packet registry.
|
||||||
|
*/
|
||||||
|
private FuzzyReflection getPacketRegistry() {
|
||||||
|
if (packetRegistry == null)
|
||||||
|
packetRegistry = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true);
|
||||||
|
return packetRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the injected proxy classes handlig each packet ID.
|
||||||
|
* @return Injected classes.
|
||||||
|
*/
|
||||||
|
public Map<Integer, Class> getOverwrittenPackets() {
|
||||||
|
return overwrittenPackets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the vanilla classes handling each packet ID.
|
||||||
|
* @return Vanilla classes.
|
||||||
|
*/
|
||||||
|
public Map<Integer, Class> getPreviousPackets() {
|
||||||
|
return previousValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve every known and supported server packet.
|
||||||
|
* @return An immutable set of every known server packet.
|
||||||
|
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
|
||||||
|
*/
|
||||||
|
public Set<Integer> getServerPackets() throws FieldAccessException {
|
||||||
|
initializeSets();
|
||||||
|
|
||||||
|
// Sanity check. This is impossible!
|
||||||
|
if (serverPackets != null && serverPackets.size() < MIN_SERVER_PACKETS)
|
||||||
|
throw new FieldAccessException("Server packet list is empty. Seems to be unsupported");
|
||||||
|
return serverPackets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve every known and supported client packet.
|
||||||
|
* @return An immutable set of every known client packet.
|
||||||
|
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
|
||||||
|
*/
|
||||||
|
public Set<Integer> getClientPackets() throws FieldAccessException {
|
||||||
|
initializeSets();
|
||||||
|
|
||||||
|
// As above
|
||||||
|
if (clientPackets != null && clientPackets.size() < MIN_CLIENT_PACKETS)
|
||||||
|
throw new FieldAccessException("Client packet list is empty. Seems to be unsupported");
|
||||||
|
return clientPackets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the correct packet class from a given packet ID.
|
||||||
|
* @param packetID - the packet ID.
|
||||||
|
* @return The associated class.
|
||||||
|
*/
|
||||||
|
public Class getPacketClassFromID(int packetID) {
|
||||||
|
return getPacketClassFromID(packetID, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the correct packet class from a given packet ID.
|
||||||
|
* @param packetID - the packet ID.
|
||||||
|
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
||||||
|
* @return The associated class.
|
||||||
|
*/
|
||||||
|
public Class getPacketClassFromID(int packetID, boolean forceVanilla) {
|
||||||
|
Map<Integer, Class> lookup = forceVanilla ? previousValues : overwrittenPackets;
|
||||||
|
Class<?> result = null;
|
||||||
|
|
||||||
|
// Optimized lookup
|
||||||
|
if (lookup.containsKey(packetID)) {
|
||||||
|
return removeEnhancer(lookup.get(packetID), forceVanilla);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh lookup tables
|
||||||
|
getPacketToID();
|
||||||
|
|
||||||
|
// See if we can look for non-vanilla classes
|
||||||
|
if (!forceVanilla) {
|
||||||
|
result = Iterables.getFirst(customIdToPacket.get(packetID), null);
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = vanillaIdToPacket.get(packetID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if we got it
|
||||||
|
if (result != null)
|
||||||
|
return result;
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("The packet ID " + packetID + " is not registered.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the packet ID of a given packet.
|
||||||
|
* @param packet - the type of packet to check.
|
||||||
|
* @return The ID of the given packet.
|
||||||
|
* @throws IllegalArgumentException If this is not a valid packet.
|
||||||
|
*/
|
||||||
|
public int getPacketID(Class<?> packet) {
|
||||||
|
if (packet == null)
|
||||||
|
throw new IllegalArgumentException("Packet type class cannot be NULL.");
|
||||||
|
if (!MinecraftReflection.getPacketClass().isAssignableFrom(packet))
|
||||||
|
throw new IllegalArgumentException("Type must be a packet.");
|
||||||
|
|
||||||
|
// The registry contains both the overridden and original packets
|
||||||
|
return getPacketToID().get(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the first superclass that is not a CBLib proxy object.
|
||||||
|
* @param clazz - the class whose hierachy we're going to search through.
|
||||||
|
* @param remove - whether or not to skip enhanced (proxy) classes.
|
||||||
|
* @return If remove is TRUE, the first superclass that is not a proxy.
|
||||||
|
*/
|
||||||
|
private static Class removeEnhancer(Class clazz, boolean remove) {
|
||||||
|
if (remove) {
|
||||||
|
// Get the underlying vanilla class
|
||||||
|
while (Factory.class.isAssignableFrom(clazz) && !clazz.equals(Object.class)) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Occurs when we were unable to retrieve all the packets in the registry.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public static class InsufficientPacketsException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final boolean client;
|
||||||
|
private final int packetCount;
|
||||||
|
|
||||||
|
private InsufficientPacketsException(String message, boolean client, int packetCount) {
|
||||||
|
super(message);
|
||||||
|
this.client = client;
|
||||||
|
this.packetCount = packetCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPacketCount() {
|
||||||
|
return packetCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CannotCorrectTroveMapException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private CannotCorrectTroveMapException(Throwable inner) {
|
||||||
|
super("Cannot correct trove map.", inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@ import com.comphenix.protocol.events.PacketContainer;
|
|||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a incoming packet injector.
|
* Represents an incoming packet injector.
|
||||||
*
|
*
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
|
@ -17,36 +17,26 @@
|
|||||||
|
|
||||||
package com.comphenix.protocol.injector.packet;
|
package com.comphenix.protocol.injector.packet;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import com.comphenix.protocol.PacketType;
|
||||||
|
|
||||||
import net.sf.cglib.proxy.Factory;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.ProtocolLibrary;
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
import com.comphenix.protocol.error.Report;
|
import com.comphenix.protocol.error.Report;
|
||||||
import com.comphenix.protocol.error.ReportType;
|
import com.comphenix.protocol.error.ReportType;
|
||||||
|
import com.comphenix.protocol.injector.netty.NettyProtocolRegistry;
|
||||||
|
import com.comphenix.protocol.injector.packet.LegacyPacketRegistry.CannotCorrectTroveMapException;
|
||||||
|
import com.comphenix.protocol.injector.packet.LegacyPacketRegistry.InsufficientPacketsException;
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
|
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
import com.comphenix.protocol.wrappers.TroveWrapper;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static packet registry in Minecraft.
|
* Static packet registry in Minecraft.
|
||||||
*
|
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@ -55,279 +45,351 @@ public class PacketRegistry {
|
|||||||
|
|
||||||
public static final ReportType REPORT_INSUFFICIENT_SERVER_PACKETS = new ReportType("Too few server packets detected: %s");
|
public static final ReportType REPORT_INSUFFICIENT_SERVER_PACKETS = new ReportType("Too few server packets detected: %s");
|
||||||
public static final ReportType REPORT_INSUFFICIENT_CLIENT_PACKETS = new ReportType("Too few client packets detected: %s");
|
public static final ReportType REPORT_INSUFFICIENT_CLIENT_PACKETS = new ReportType("Too few client packets detected: %s");
|
||||||
|
|
||||||
private static final int MIN_SERVER_PACKETS = 5;
|
|
||||||
private static final int MIN_CLIENT_PACKETS = 5;
|
|
||||||
|
|
||||||
// Fuzzy reflection
|
// Two different packet registry
|
||||||
private static FuzzyReflection packetRegistry;
|
private static volatile LegacyPacketRegistry LEGACY;
|
||||||
|
private static volatile NettyProtocolRegistry NETTY;
|
||||||
|
|
||||||
// The packet class to packet ID translator
|
// Cached for legacy
|
||||||
private static Map<Class, Integer> packetToID;
|
private static volatile Set<PacketType> NETTY_SERVER_PACKETS;
|
||||||
|
private static volatile Set<PacketType> NETTY_CLIENT_PACKETS;
|
||||||
|
|
||||||
// Packet IDs to classes, grouped by whether or not they're vanilla or custom defined
|
// Cached for Netty
|
||||||
private static Multimap<Integer, Class> customIdToPacket;
|
private static volatile Set<Integer> LEGACY_SERVER_PACKETS;
|
||||||
private static Map<Integer, Class> vanillaIdToPacket;
|
private static volatile Set<Integer> LEGACY_CLIENT_PACKETS;
|
||||||
|
private static volatile Map<Integer, Class> LEGACY_PREVIOUS_PACKETS;
|
||||||
|
|
||||||
// Whether or not certain packets are sent by the client or the server
|
// Whether or not the registry has
|
||||||
private static ImmutableSet<Integer> serverPackets;
|
private static boolean INITIALIZED;
|
||||||
private static ImmutableSet<Integer> clientPackets;
|
|
||||||
|
|
||||||
// The underlying sets
|
/**
|
||||||
private static Set<Integer> serverPacketsRef;
|
* Initialize the packet registry.
|
||||||
private static Set<Integer> clientPacketsRef;
|
*/
|
||||||
|
private static void initialize() {
|
||||||
// New proxy values
|
if (INITIALIZED) {
|
||||||
private static Map<Integer, Class> overwrittenPackets = new HashMap<Integer, Class>();
|
// Make sure they were initialized
|
||||||
|
if (NETTY == null && LEGACY == null)
|
||||||
// Vanilla packets
|
throw new IllegalStateException("No initialized registry.");
|
||||||
private static Map<Integer, Class> previousValues = new HashMap<Integer, Class>();
|
return;
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked" })
|
|
||||||
public static Map<Class, Integer> getPacketToID() {
|
|
||||||
// Initialize it, if we haven't already
|
|
||||||
if (packetToID == null) {
|
|
||||||
try {
|
|
||||||
Field packetsField = getPacketRegistry().getFieldByType("packetsField", Map.class);
|
|
||||||
packetToID = (Map<Class, Integer>) FieldUtils.readStaticField(packetsField, true);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// Spigot 1.2.5 MCPC workaround
|
|
||||||
try {
|
|
||||||
packetToID = getSpigotWrapper();
|
|
||||||
} catch (Exception e2) {
|
|
||||||
// Very bad indeed
|
|
||||||
throw new IllegalArgumentException(e.getMessage() + "; Spigot workaround failed.", e2);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException("Unable to retrieve the packetClassToIdMap", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the inverse maps
|
|
||||||
customIdToPacket = InverseMaps.inverseMultimap(packetToID, new Predicate<Map.Entry<Class, Integer>>() {
|
|
||||||
@Override
|
|
||||||
public boolean apply(@Nullable Entry<Class, Integer> entry) {
|
|
||||||
return !MinecraftReflection.isMinecraftClass(entry.getKey());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// And the vanilla pack - here we assume a unique ID to class mapping
|
|
||||||
vanillaIdToPacket = InverseMaps.inverseMap(packetToID, new Predicate<Map.Entry<Class, Integer>>() {
|
|
||||||
@Override
|
|
||||||
public boolean apply(@Nullable Entry<Class, Integer> entry) {
|
|
||||||
return MinecraftReflection.isMinecraftClass(entry.getKey());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return packetToID;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<Class, Integer> getSpigotWrapper() throws IllegalAccessException {
|
// Check for netty
|
||||||
// If it talks like a duck, etc.
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
// Perhaps it would be nice to have a proper duck typing library as well
|
if (NETTY == null) {
|
||||||
FuzzyClassContract mapLike = FuzzyClassContract.newBuilder().
|
NETTY = new NettyProtocolRegistry();
|
||||||
method(FuzzyMethodContract.newBuilder().
|
|
||||||
nameExact("size").returnTypeExact(int.class)).
|
|
||||||
method(FuzzyMethodContract.newBuilder().
|
|
||||||
nameExact("put").parameterCount(2)).
|
|
||||||
method(FuzzyMethodContract.newBuilder().
|
|
||||||
nameExact("get").parameterCount(1)).
|
|
||||||
build();
|
|
||||||
|
|
||||||
Field packetsField = getPacketRegistry().getField(
|
|
||||||
FuzzyFieldContract.newBuilder().typeMatches(mapLike).build());
|
|
||||||
Object troveMap = FieldUtils.readStaticField(packetsField, true);
|
|
||||||
|
|
||||||
// Check for stupid no_entry_values
|
|
||||||
try {
|
|
||||||
Field field = FieldUtils.getField(troveMap.getClass(), "no_entry_value", true);
|
|
||||||
Integer value = (Integer) FieldUtils.readField(field, troveMap, true);
|
|
||||||
|
|
||||||
if (value >= 0 && value < 256) {
|
|
||||||
// Someone forgot to set the no entry value. Let's help them.
|
|
||||||
FieldUtils.writeField(field, troveMap, -1);
|
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} else {
|
||||||
// Whatever
|
initializeLegacy();
|
||||||
ProtocolLibrary.getErrorReporter().reportWarning(PacketRegistry.class,
|
|
||||||
Report.newBuilder(REPORT_CANNOT_CORRECT_TROVE_MAP).error(e));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll assume this a Trove map
|
|
||||||
return TroveWrapper.getDecoratedMap(troveMap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the cached fuzzy reflection instance allowing access to the packet registry.
|
* Determine if the given packet type is supported on the current server.
|
||||||
* @return Reflected packet registry.
|
* @param type - the type to check.
|
||||||
*/
|
* @return TRUE if it is, FALSE otherwise.
|
||||||
private static FuzzyReflection getPacketRegistry() {
|
*/
|
||||||
if (packetRegistry == null)
|
public static boolean isSupported(PacketType type) {
|
||||||
packetRegistry = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true);
|
initialize();
|
||||||
return packetRegistry;
|
|
||||||
|
if (NETTY != null)
|
||||||
|
return NETTY.getPacketTypeLookup().containsKey(type);
|
||||||
|
|
||||||
|
// Look up the correct type
|
||||||
|
return type.isClient() ?
|
||||||
|
LEGACY.getClientPackets().contains(type.getLegacyId()) :
|
||||||
|
LEGACY.getServerPackets().contains(type.getLegacyId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the legacy packet registry.
|
||||||
|
*/
|
||||||
|
private static void initializeLegacy() {
|
||||||
|
if (LEGACY == null) {
|
||||||
|
try {
|
||||||
|
LEGACY = new LegacyPacketRegistry();
|
||||||
|
LEGACY.initialize();
|
||||||
|
} catch (InsufficientPacketsException e) {
|
||||||
|
if (e.isClient()) {
|
||||||
|
ProtocolLibrary.getErrorReporter().reportWarning(
|
||||||
|
PacketRegistry.class, Report.newBuilder(REPORT_INSUFFICIENT_CLIENT_PACKETS).messageParam(e.getPacketCount())
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ProtocolLibrary.getErrorReporter().reportWarning(
|
||||||
|
PacketRegistry.class, Report.newBuilder(REPORT_INSUFFICIENT_SERVER_PACKETS).messageParam(e.getPacketCount())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (CannotCorrectTroveMapException e) {
|
||||||
|
ProtocolLibrary.getErrorReporter().reportWarning(PacketRegistry.class,
|
||||||
|
Report.newBuilder(REPORT_CANNOT_CORRECT_TROVE_MAP).error(e.getCause()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a map of every packet class to every ID.
|
||||||
|
* <p>
|
||||||
|
* Deprecated: Use {@link #getPacketToType()} instead.
|
||||||
|
* @return A map of packet classes and their corresponding ID.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static Map<Class, Integer> getPacketToID() {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
if (NETTY != null) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<Class, Integer> result = (Map)Maps.transformValues(NETTY.getPacketClassLookup(), new Function<PacketType, Integer>() {
|
||||||
|
public Integer apply(PacketType type) {
|
||||||
|
return type.getLegacyId();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return LEGACY.getPacketToID();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a map of every packet class to the respective packet type.
|
||||||
|
* @return A map of packet classes and their corresponding packet type.
|
||||||
|
*/
|
||||||
|
public static Map<Class, PacketType> getPacketToType() {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
if (NETTY != null) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<Class, PacketType> result = (Map)NETTY.getPacketClassLookup();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return Maps.transformValues(LEGACY.getPacketToID(), new Function<Integer, PacketType>() {
|
||||||
|
public PacketType apply(Integer packetId) {
|
||||||
|
return PacketType.findLegacy(packetId);
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the injected proxy classes handlig each packet ID.
|
* Retrieve the injected proxy classes handlig each packet ID.
|
||||||
|
* <p>
|
||||||
|
* This is not supported in 1.7.2 and later.
|
||||||
* @return Injected classes.
|
* @return Injected classes.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static Map<Integer, Class> getOverwrittenPackets() {
|
public static Map<Integer, Class> getOverwrittenPackets() {
|
||||||
return overwrittenPackets;
|
initialize();
|
||||||
|
|
||||||
|
if (LEGACY != null)
|
||||||
|
return LEGACY.getOverwrittenPackets();
|
||||||
|
throw new IllegalStateException("Not supported on Netty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the vanilla classes handling each packet ID.
|
* Retrieve the vanilla classes handling each packet ID.
|
||||||
* @return Vanilla classes.
|
* @return Vanilla classes.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static Map<Integer, Class> getPreviousPackets() {
|
public static Map<Integer, Class> getPreviousPackets() {
|
||||||
return previousValues;
|
initialize();
|
||||||
|
|
||||||
|
if (NETTY != null) {
|
||||||
|
// Construct it first
|
||||||
|
if (LEGACY_PREVIOUS_PACKETS == null) {
|
||||||
|
Map<Integer, Class> map = Maps.newHashMap();
|
||||||
|
|
||||||
|
for (Entry<PacketType, Class<?>> entry : NETTY.getPacketTypeLookup().entrySet()) {
|
||||||
|
map.put(entry.getKey().getLegacyId(), entry.getValue());
|
||||||
|
}
|
||||||
|
LEGACY_PREVIOUS_PACKETS = Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
return LEGACY_PREVIOUS_PACKETS;
|
||||||
|
}
|
||||||
|
return LEGACY.getPreviousPackets();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve every known and supported server packet.
|
* Retrieve every known and supported server packet.
|
||||||
|
* <p>
|
||||||
|
* Deprecated: Use {@link #getServerPacketTypes()} instead.
|
||||||
* @return An immutable set of every known server packet.
|
* @return An immutable set of every known server packet.
|
||||||
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
|
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static Set<Integer> getServerPackets() throws FieldAccessException {
|
public static Set<Integer> getServerPackets() throws FieldAccessException {
|
||||||
initializeSets();
|
initialize();
|
||||||
|
|
||||||
// Sanity check. This is impossible!
|
if (NETTY != null) {
|
||||||
if (serverPackets != null && serverPackets.size() < MIN_SERVER_PACKETS)
|
if (LEGACY_SERVER_PACKETS == null) {
|
||||||
throw new FieldAccessException("Server packet list is empty. Seems to be unsupported");
|
LEGACY_SERVER_PACKETS = toLegacy(NETTY.getServerPackets());
|
||||||
return serverPackets;
|
}
|
||||||
|
return LEGACY_SERVER_PACKETS;
|
||||||
|
}
|
||||||
|
return LEGACY.getServerPackets();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve every known and supported server packet type.
|
||||||
|
* @return Every server packet type.
|
||||||
|
*/
|
||||||
|
public static Set<PacketType> getServerPacketTypes() {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
if (NETTY != null)
|
||||||
|
return NETTY.getServerPackets();
|
||||||
|
|
||||||
|
// Handle legacy
|
||||||
|
if (NETTY_SERVER_PACKETS == null) {
|
||||||
|
NETTY_SERVER_PACKETS = toPacketTypes(LEGACY.getServerPackets());
|
||||||
|
}
|
||||||
|
return NETTY_SERVER_PACKETS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve every known and supported client packet.
|
* Retrieve every known and supported client packet.
|
||||||
|
* <p>
|
||||||
|
* Deprecated: Use {@link #getClientPacketTypes()} instead.
|
||||||
* @return An immutable set of every known client packet.
|
* @return An immutable set of every known client packet.
|
||||||
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
|
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static Set<Integer> getClientPackets() throws FieldAccessException {
|
public static Set<Integer> getClientPackets() throws FieldAccessException {
|
||||||
initializeSets();
|
initialize();
|
||||||
|
|
||||||
// As above
|
if (NETTY != null) {
|
||||||
if (clientPackets != null && clientPackets.size() < MIN_CLIENT_PACKETS)
|
if (LEGACY_CLIENT_PACKETS == null) {
|
||||||
throw new FieldAccessException("Client packet list is empty. Seems to be unsupported");
|
LEGACY_CLIENT_PACKETS = toLegacy(NETTY.getClientPackets());
|
||||||
return clientPackets;
|
}
|
||||||
|
return LEGACY_CLIENT_PACKETS;
|
||||||
|
}
|
||||||
|
return LEGACY.getClientPackets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
/**
|
||||||
private static void initializeSets() throws FieldAccessException {
|
* Retrieve every known and supported server packet type.
|
||||||
if (serverPacketsRef == null || clientPacketsRef == null) {
|
* @return Every server packet type.
|
||||||
List<Field> sets = getPacketRegistry().getFieldListByType(Set.class);
|
*/
|
||||||
|
public static Set<PacketType> getClientPacketTypes() {
|
||||||
try {
|
initialize();
|
||||||
if (sets.size() > 1) {
|
|
||||||
serverPacketsRef = (Set<Integer>) FieldUtils.readStaticField(sets.get(0), true);
|
if (NETTY != null)
|
||||||
clientPacketsRef = (Set<Integer>) FieldUtils.readStaticField(sets.get(1), true);
|
return NETTY.getClientPackets();
|
||||||
|
|
||||||
// Impossible
|
// Handle legacy
|
||||||
if (serverPacketsRef == null || clientPacketsRef == null)
|
if (NETTY_CLIENT_PACKETS == null) {
|
||||||
throw new FieldAccessException("Packet sets are in an illegal state.");
|
NETTY_CLIENT_PACKETS = toPacketTypes(LEGACY.getClientPackets());
|
||||||
|
|
||||||
// NEVER allow callers to modify the underlying sets
|
|
||||||
serverPackets = ImmutableSet.copyOf(serverPacketsRef);
|
|
||||||
clientPackets = ImmutableSet.copyOf(clientPacketsRef);
|
|
||||||
|
|
||||||
// Check sizes
|
|
||||||
if (serverPackets.size() < MIN_SERVER_PACKETS)
|
|
||||||
ProtocolLibrary.getErrorReporter().reportWarning(
|
|
||||||
PacketRegistry.class, Report.newBuilder(REPORT_INSUFFICIENT_SERVER_PACKETS).messageParam(serverPackets.size())
|
|
||||||
);
|
|
||||||
if (clientPackets.size() < MIN_CLIENT_PACKETS)
|
|
||||||
ProtocolLibrary.getErrorReporter().reportWarning(
|
|
||||||
PacketRegistry.class, Report.newBuilder(REPORT_INSUFFICIENT_CLIENT_PACKETS).messageParam(clientPackets.size())
|
|
||||||
);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new FieldAccessException("Cannot retrieve packet client/server sets.");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new FieldAccessException("Cannot access field.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Copy over again if it has changed
|
|
||||||
if (serverPacketsRef != null && serverPacketsRef.size() != serverPackets.size())
|
|
||||||
serverPackets = ImmutableSet.copyOf(serverPacketsRef);
|
|
||||||
if (clientPacketsRef != null && clientPacketsRef.size() != clientPackets.size())
|
|
||||||
clientPackets = ImmutableSet.copyOf(clientPacketsRef);
|
|
||||||
}
|
}
|
||||||
|
return NETTY_CLIENT_PACKETS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a set of packet types to a set of integers based on the legacy packet ID.
|
||||||
|
* @param types - packet type.
|
||||||
|
* @return Set of integers.
|
||||||
|
*/
|
||||||
|
private static Set<Integer> toLegacy(Set<PacketType> types) {
|
||||||
|
Set<Integer> result = Sets.newHashSet();
|
||||||
|
|
||||||
|
for (PacketType type : types)
|
||||||
|
result.add(type.getLegacyId());
|
||||||
|
return Collections.unmodifiableSet(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a set of legacy packet IDs to packet types.
|
||||||
|
* @param types - legacy packet IDs.
|
||||||
|
* @return Set of packet types.
|
||||||
|
*/
|
||||||
|
private static Set<PacketType> toPacketTypes(Set<Integer> ids) {
|
||||||
|
Set<PacketType> result = Sets.newHashSet();
|
||||||
|
|
||||||
|
for (int id : ids)
|
||||||
|
result.add(PacketType.findLegacy(id));
|
||||||
|
return Collections.unmodifiableSet(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the correct packet class from a given packet ID.
|
* Retrieves the correct packet class from a given packet ID.
|
||||||
|
* <p>
|
||||||
|
* Deprecated: Use {@link #getPacketClassFromType(PacketType)} instead.
|
||||||
* @param packetID - the packet ID.
|
* @param packetID - the packet ID.
|
||||||
* @return The associated class.
|
* @return The associated class.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static Class getPacketClassFromID(int packetID) {
|
public static Class getPacketClassFromID(int packetID) {
|
||||||
return getPacketClassFromID(packetID, false);
|
initialize();
|
||||||
|
|
||||||
|
if (NETTY != null)
|
||||||
|
return NETTY.getPacketTypeLookup().get(PacketType.findLegacy(packetID));
|
||||||
|
return LEGACY.getPacketClassFromID(packetID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the correct packet class from a given type.
|
||||||
|
* @param type - the packet type.
|
||||||
|
* @return The associated class.
|
||||||
|
*/
|
||||||
|
public static Class getPacketClassFromType(PacketType type) {
|
||||||
|
return getPacketClassFromType(type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the correct packet class from a given type.
|
||||||
|
* <p>
|
||||||
|
* Note that forceVanillla will be ignored on MC 1.7.2 and later.
|
||||||
|
* @param type - the packet type.
|
||||||
|
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
||||||
|
* @return The associated class.
|
||||||
|
*/
|
||||||
|
public static Class getPacketClassFromType(PacketType type, boolean forceVanilla) {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
if (NETTY != null)
|
||||||
|
return NETTY.getPacketTypeLookup().get(type);
|
||||||
|
return LEGACY.getPacketClassFromID(type.getLegacyId(), forceVanilla);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the correct packet class from a given packet ID.
|
* Retrieves the correct packet class from a given packet ID.
|
||||||
|
* <p>
|
||||||
|
* This method has been deprecated.
|
||||||
* @param packetID - the packet ID.
|
* @param packetID - the packet ID.
|
||||||
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
||||||
* @return The associated class.
|
* @return The associated class.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static Class getPacketClassFromID(int packetID, boolean forceVanilla) {
|
public static Class getPacketClassFromID(int packetID, boolean forceVanilla) {
|
||||||
Map<Integer, Class> lookup = forceVanilla ? previousValues : overwrittenPackets;
|
initialize();
|
||||||
Class<?> result = null;
|
|
||||||
|
|
||||||
// Optimized lookup
|
if (LEGACY != null)
|
||||||
if (lookup.containsKey(packetID)) {
|
return LEGACY.getPacketClassFromID(packetID, forceVanilla);
|
||||||
return removeEnhancer(lookup.get(packetID), forceVanilla);
|
return getPacketClassFromID(packetID);
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh lookup tables
|
|
||||||
getPacketToID();
|
|
||||||
|
|
||||||
// See if we can look for non-vanilla classes
|
|
||||||
if (!forceVanilla) {
|
|
||||||
result = Iterables.getFirst(customIdToPacket.get(packetID), null);
|
|
||||||
}
|
|
||||||
if (result == null) {
|
|
||||||
result = vanillaIdToPacket.get(packetID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if we got it
|
|
||||||
if (result != null)
|
|
||||||
return result;
|
|
||||||
else
|
|
||||||
throw new IllegalArgumentException("The packet ID " + packetID + " is not registered.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the packet ID of a given packet.
|
* Retrieve the packet ID of a given packet.
|
||||||
|
* <p>
|
||||||
|
* Deprecated: Use {@link #getPacketType(Class)}.
|
||||||
* @param packet - the type of packet to check.
|
* @param packet - the type of packet to check.
|
||||||
* @return The ID of the given packet.
|
* @return The legacy ID of the given packet.
|
||||||
* @throws IllegalArgumentException If this is not a valid packet.
|
* @throws IllegalArgumentException If this is not a valid packet.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static int getPacketID(Class<?> packet) {
|
public static int getPacketID(Class<?> packet) {
|
||||||
if (packet == null)
|
initialize();
|
||||||
throw new IllegalArgumentException("Packet type class cannot be NULL.");
|
|
||||||
if (!MinecraftReflection.getPacketClass().isAssignableFrom(packet))
|
|
||||||
throw new IllegalArgumentException("Type must be a packet.");
|
|
||||||
|
|
||||||
// The registry contains both the overridden and original packets
|
if (NETTY != null)
|
||||||
return getPacketToID().get(packet);
|
return NETTY.getPacketClassLookup().get(packet).getLegacyId();
|
||||||
|
return LEGACY.getPacketID(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the first superclass that is not a CBLib proxy object.
|
* Retrieve the packet type of a given packet.
|
||||||
* @param clazz - the class whose hierachy we're going to search through.
|
* @param packet - the class of the packet.
|
||||||
* @param remove - whether or not to skip enhanced (proxy) classes.
|
* @return The packet type.
|
||||||
* @return If remove is TRUE, the first superclass that is not a proxy.
|
* @throws IllegalArgumentException If this is not a valid packet.
|
||||||
*/
|
*/
|
||||||
private static Class removeEnhancer(Class clazz, boolean remove) {
|
public static PacketType getPacketType(Class<?> packet) {
|
||||||
if (remove) {
|
initialize();
|
||||||
// Get the underlying vanilla class
|
|
||||||
while (Factory.class.isAssignableFrom(clazz) && !clazz.equals(Object.class)) {
|
|
||||||
clazz = clazz.getSuperclass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return clazz;
|
if (NETTY != null)
|
||||||
|
return NETTY.getPacketClassLookup().get(packet);
|
||||||
|
return PacketType.findLegacy(LEGACY.getPacketID(packet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.comphenix.protocol.injector.spigot;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||||
|
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||||
|
|
||||||
|
public abstract class AbstractPacketInjector implements PacketInjector {
|
||||||
|
private IntegerSet reveivedFilters;
|
||||||
|
|
||||||
|
public AbstractPacketInjector(IntegerSet reveivedFilters) {
|
||||||
|
this.reveivedFilters = reveivedFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled(Object packet) {
|
||||||
|
// No, it's never cancelled
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCancelled(Object packet, boolean cancelled) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addPacketHandler(int packetID) {
|
||||||
|
reveivedFilters.add(packetID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removePacketHandler(int packetID) {
|
||||||
|
reveivedFilters.remove(packetID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPacketHandler(int packetID) {
|
||||||
|
return reveivedFilters.contains(packetID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Integer> getPacketHandlers() {
|
||||||
|
return reveivedFilters.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanupAll() {
|
||||||
|
reveivedFilters.clear();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package com.comphenix.protocol.injector.spigot;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||||
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
|
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||||
|
|
||||||
|
public abstract class AbstractPlayerHandler implements PlayerInjectionHandler {
|
||||||
|
protected IntegerSet sendingFilters;
|
||||||
|
|
||||||
|
public AbstractPlayerHandler(IntegerSet sendingFilters) {
|
||||||
|
this.sendingFilters = sendingFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPlayerHook(GamePhase phase, PlayerInjectHooks playerHook) {
|
||||||
|
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
||||||
|
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPacketHandler(int packetID) {
|
||||||
|
sendingFilters.add(packetID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removePacketHandler(int packetID) {
|
||||||
|
sendingFilters.remove(packetID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Integer> getSendingFilters() {
|
||||||
|
return sendingFilters.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
sendingFilters.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlayerInjectHooks getPlayerHook(GamePhase phase) {
|
||||||
|
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canRecievePackets() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlayerInjectHooks getPlayerHook() {
|
||||||
|
// Pretend that we do
|
||||||
|
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Player getPlayerByConnection(DataInputStream inputStream) throws InterruptedException {
|
||||||
|
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkListener(PacketListener listener) {
|
||||||
|
// They're all fine!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkListener(Set<PacketListener> listeners) {
|
||||||
|
// Yes, really
|
||||||
|
}
|
||||||
|
}
|
@ -16,28 +16,15 @@ import com.google.common.collect.Sets;
|
|||||||
*
|
*
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
class DummyPacketInjector implements PacketInjector {
|
class DummyPacketInjector extends AbstractPacketInjector implements PacketInjector {
|
||||||
private SpigotPacketInjector injector;
|
private SpigotPacketInjector injector;
|
||||||
private IntegerSet reveivedFilters;
|
|
||||||
|
|
||||||
private IntegerSet lastBufferedPackets = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
|
private IntegerSet lastBufferedPackets = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
|
||||||
|
|
||||||
public DummyPacketInjector(SpigotPacketInjector injector, IntegerSet reveivedFilters) {
|
public DummyPacketInjector(SpigotPacketInjector injector, IntegerSet reveivedFilters) {
|
||||||
|
super(reveivedFilters);
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
this.reveivedFilters = reveivedFilters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCancelled(Object packet) {
|
|
||||||
// No, it's never cancelled
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCancelled(Object packet, boolean cancelled) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void inputBuffersChanged(Set<Integer> set) {
|
public void inputBuffersChanged(Set<Integer> set) {
|
||||||
Set<Integer> removed = Sets.difference(lastBufferedPackets.toSet(), set);
|
Set<Integer> removed = Sets.difference(lastBufferedPackets.toSet(), set);
|
||||||
@ -52,35 +39,8 @@ class DummyPacketInjector implements PacketInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean addPacketHandler(int packetID) {
|
|
||||||
reveivedFilters.add(packetID);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removePacketHandler(int packetID) {
|
|
||||||
reveivedFilters.remove(packetID);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPacketHandler(int packetID) {
|
|
||||||
return reveivedFilters.contains(packetID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Integer> getPacketHandlers() {
|
|
||||||
return reveivedFilters.toSet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
|
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
|
||||||
return injector.packetReceived(packet, client, buffered);
|
return injector.packetReceived(packet, client, buffered);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cleanupAll() {
|
|
||||||
reveivedFilters.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,25 @@
|
|||||||
package com.comphenix.protocol.injector.spigot;
|
package com.comphenix.protocol.injector.spigot;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Set;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.concurrency.IntegerSet;
|
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||||
import com.comphenix.protocol.events.NetworkMarker;
|
import com.comphenix.protocol.events.NetworkMarker;
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
|
||||||
import com.comphenix.protocol.injector.GamePhase;
|
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
|
||||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dummy player handler that simply delegates to its parent Spigot packet injector.
|
* Dummy player handler that simply delegates to its parent Spigot packet injector.
|
||||||
*
|
*
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
class DummyPlayerHandler implements PlayerInjectionHandler {
|
class DummyPlayerHandler extends AbstractPlayerHandler {
|
||||||
private SpigotPacketInjector injector;
|
private SpigotPacketInjector injector;
|
||||||
private IntegerSet sendingFilters;
|
|
||||||
|
|
||||||
public DummyPlayerHandler(SpigotPacketInjector injector, IntegerSet sendingFilters) {
|
public DummyPlayerHandler(SpigotPacketInjector injector, IntegerSet sendingFilters) {
|
||||||
|
super(sendingFilters);
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
this.sendingFilters = sendingFilters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -41,36 +33,6 @@ class DummyPlayerHandler implements PlayerInjectionHandler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setPlayerHook(GamePhase phase, PlayerInjectHooks playerHook) {
|
|
||||||
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
|
||||||
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addPacketHandler(int packetID) {
|
|
||||||
sendingFilters.add(packetID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removePacketHandler(int packetID) {
|
|
||||||
sendingFilters.remove(packetID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Integer> getSendingFilters() {
|
|
||||||
return sendingFilters.toSet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
sendingFilters.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendServerPacket(Player reciever, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
|
public void sendServerPacket(Player reciever, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
|
||||||
injector.sendServerPacket(reciever, packet, marker, filters);
|
injector.sendServerPacket(reciever, packet, marker, filters);
|
||||||
@ -92,16 +54,6 @@ class DummyPlayerHandler implements PlayerInjectionHandler {
|
|||||||
// Just ignore
|
// Just ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PlayerInjectHooks getPlayerHook(GamePhase phase) {
|
|
||||||
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canRecievePackets() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PacketEvent handlePacketRecieved(PacketContainer packet, InputStream input, byte[] buffered) {
|
public PacketEvent handlePacketRecieved(PacketContainer packet, InputStream input, byte[] buffered) {
|
||||||
// Associate this buffered data
|
// Associate this buffered data
|
||||||
@ -111,27 +63,6 @@ class DummyPlayerHandler implements PlayerInjectionHandler {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PlayerInjectHooks getPlayerHook() {
|
|
||||||
// Pretend that we do
|
|
||||||
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Player getPlayerByConnection(DataInputStream inputStream) throws InterruptedException {
|
|
||||||
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkListener(PacketListener listener) {
|
|
||||||
// They're all fine!
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkListener(Set<PacketListener> listeners) {
|
|
||||||
// Yes, really
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updatePlayer(Player player) {
|
public void updatePlayer(Player player) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
@ -19,6 +19,7 @@ package com.comphenix.protocol.reflect;
|
|||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -28,6 +29,8 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import net.minecraft.util.com.google.common.collect.Sets;
|
||||||
|
|
||||||
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
|
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
@ -38,6 +41,33 @@ import com.google.common.collect.Maps;
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class FuzzyReflection {
|
public class FuzzyReflection {
|
||||||
|
/**
|
||||||
|
* Represents an interface for accessing a field.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public interface FieldAccessor {
|
||||||
|
/**
|
||||||
|
* Retrieve the value of a field for a particular instance.
|
||||||
|
* @param instance - the instance, or NULL for a static field.
|
||||||
|
* @return The value of the field.
|
||||||
|
* @throws IllegalStateException If the current security context prohibits reflection.
|
||||||
|
*/
|
||||||
|
public Object get(Object instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an interface for invoking a method.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public interface MethodAccessor {
|
||||||
|
/**
|
||||||
|
* Invoke the underlying method.
|
||||||
|
* @param target - the target instance, or NULL for a static method.
|
||||||
|
* @param args - the arguments to pass to the method.
|
||||||
|
* @return The return value, or NULL for void methods.
|
||||||
|
*/
|
||||||
|
public Object invoke(Object target, Object... args);
|
||||||
|
}
|
||||||
|
|
||||||
// The class we're actually representing
|
// The class we're actually representing
|
||||||
private Class<?> source;
|
private Class<?> source;
|
||||||
@ -88,12 +118,132 @@ public class FuzzyReflection {
|
|||||||
return new FuzzyReflection(reference.getClass(), forceAccess);
|
return new FuzzyReflection(reference.getClass(), forceAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an accessor for the first field of the given type.
|
||||||
|
* @param instanceClass - the type of the instance to retrieve.
|
||||||
|
* @param fieldClass - type of the field to retrieve.
|
||||||
|
* @param forceAccess - whether or not to look for private and protected fields.
|
||||||
|
* @return The value of that field.
|
||||||
|
* @throws IllegalArgumentException If the field cannot be found.
|
||||||
|
*/
|
||||||
|
public static FieldAccessor getFieldAccessor(Class<?> instanceClass, Class<?> fieldClass, boolean forceAccess) {
|
||||||
|
// Get a field accessor
|
||||||
|
Field field = FuzzyReflection.fromObject(instanceClass, forceAccess).getFieldByType(null, fieldClass);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return getFieldAccessor(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a field accessor from a given field that uses unchecked exceptions.
|
||||||
|
* @param field - the field.
|
||||||
|
* @return The field accessor.
|
||||||
|
*/
|
||||||
|
public static FieldAccessor getFieldAccessor(final Field field) {
|
||||||
|
return new FieldAccessor() {
|
||||||
|
@Override
|
||||||
|
public Object get(Object instance) {
|
||||||
|
try {
|
||||||
|
return field.get(instance);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IllegalStateException("Cannot use reflection.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a method accessor for a method with the given name and signature.
|
||||||
|
* @param instanceClass - the parent class.
|
||||||
|
* @param name - the method name.
|
||||||
|
* @param parameters - the parameters.
|
||||||
|
* @return The method accessor.
|
||||||
|
*/
|
||||||
|
public static MethodAccessor getMethodAccessor(Class<?> instanceClass, String name, Class<?>... parameters) {
|
||||||
|
Method method = MethodUtils.getAccessibleMethod(instanceClass, name, parameters);
|
||||||
|
method.setAccessible(true);
|
||||||
|
return getMethodAccessor(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a method accessor for a particular method, avoding checked exceptions.
|
||||||
|
* @param method - the method to access.
|
||||||
|
* @return The method accessor.
|
||||||
|
*/
|
||||||
|
public static MethodAccessor getMethodAccessor(final Method method) {
|
||||||
|
return new MethodAccessor() {
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object target, Object... args) {
|
||||||
|
try {
|
||||||
|
return method.invoke(target, args);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IllegalStateException("Cannot use reflection.", e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new RuntimeException("An internal error occured.", e.getCause());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the value of the first field of the given type.
|
||||||
|
* @param instance - the instance to retrieve from.
|
||||||
|
* @param fieldClass - type of the field to retrieve.
|
||||||
|
* @param forceAccess - whether or not to look for private and protected fields.
|
||||||
|
* @return The value of that field.
|
||||||
|
* @throws IllegalArgumentException If the field cannot be found.
|
||||||
|
*/
|
||||||
|
public static <T> T getFieldValue(Object instance, Class<T> fieldClass, boolean forceAccess) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T result = (T) getFieldAccessor(instance.getClass(), fieldClass, forceAccess).get(instance);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the underlying class.
|
* Retrieves the underlying class.
|
||||||
*/
|
*/
|
||||||
public Class<?> getSource() {
|
public Class<?> getSource() {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the singleton instance of a class, from a method or field.
|
||||||
|
* @return The singleton instance.
|
||||||
|
* @throws IllegalStateException If the class has no singleton.
|
||||||
|
*/
|
||||||
|
public Object getSingleton() {
|
||||||
|
Method method = null;
|
||||||
|
Field field = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
method = getMethodByParameters("getInstance", source.getClass(), new Class<?>[0]);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Try getting the field instead
|
||||||
|
// Note that this will throw an exception if not found
|
||||||
|
field = getFieldByType("instance", source.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert into unchecked exceptions
|
||||||
|
if (method != null) {
|
||||||
|
try {
|
||||||
|
method.setAccessible(true);
|
||||||
|
return method.invoke(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Cannot invoke singleton method " + method, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (field != null) {
|
||||||
|
try {
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("Cannot get content of singleton field " + field, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We should never get to this point
|
||||||
|
throw new IllegalStateException("Impossible.");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the first method that matches.
|
* Retrieve the first method that matches.
|
||||||
@ -246,7 +396,6 @@ public class FuzzyReflection {
|
|||||||
methods.add(method);
|
methods.add(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return methods;
|
return methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,6 +622,25 @@ public class FuzzyReflection {
|
|||||||
return setUnion(source.getFields());
|
return setUnion(source.getFields());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all private and public fields, up until a certain superclass.
|
||||||
|
* @param excludeClass - the class (and its superclasses) to exclude from the search.
|
||||||
|
* @return Every such declared field.
|
||||||
|
*/
|
||||||
|
public Set<Field> getDeclaredFields(Class<?> excludeClass) {
|
||||||
|
if (forceAccess) {
|
||||||
|
Class<?> current = source;
|
||||||
|
Set<Field> fields = Sets.newLinkedHashSet();
|
||||||
|
|
||||||
|
while (current != null && current != excludeClass) {
|
||||||
|
fields.addAll(Arrays.asList(current.getDeclaredFields()));
|
||||||
|
current = current.getSuperclass();
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
return getFields();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all private and public methods in declared order (after JDK 1.5).
|
* Retrieves all private and public methods in declared order (after JDK 1.5).
|
||||||
* <p>
|
* <p>
|
||||||
@ -509,7 +677,6 @@ public class FuzzyReflection {
|
|||||||
result.add(element);
|
result.add(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
package com.comphenix.protocol.reflect;
|
package com.comphenix.protocol.reflect;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.google.common.collect.BiMap;
|
import com.google.common.collect.BiMap;
|
||||||
@ -31,27 +33,32 @@ import com.google.common.collect.HashBiMap;
|
|||||||
* want to prevent the creation of additional members dynamically.
|
* want to prevent the creation of additional members dynamically.
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class ObjectEnum<T> {
|
public class ObjectEnum<T> implements Iterable<T> {
|
||||||
// Used to convert between IDs and names
|
// Used to convert between IDs and names
|
||||||
protected BiMap<T, String> members = HashBiMap.create();
|
protected BiMap<T, String> members = HashBiMap.create();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers every declared integer field.
|
* Registers every declared integer field.
|
||||||
*/
|
*/
|
||||||
public ObjectEnum() {
|
public ObjectEnum(Class<T> fieldType) {
|
||||||
registerAll();
|
registerAll(fieldType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers every public int field as a member.
|
* Registers every public assignable static field as a member.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected void registerAll() {
|
protected void registerAll(Class<T> fieldType) {
|
||||||
try {
|
try {
|
||||||
// Register every int field
|
// Register every int field
|
||||||
for (Field entry : this.getClass().getFields()) {
|
for (Field entry : this.getClass().getFields()) {
|
||||||
if (entry.getType().equals(int.class)) {
|
if (Modifier.isStatic(entry.getModifiers()) && fieldType.isAssignableFrom(entry.getType())) {
|
||||||
registerMember((T) entry.get(this), entry.getName());
|
T value = (T) entry.get(null);
|
||||||
|
|
||||||
|
if (value == null)
|
||||||
|
throw new IllegalArgumentException("Field " + entry + " was NULL. Remember to " +
|
||||||
|
"construct the object after the field has been declared.");
|
||||||
|
registerMember(value, entry.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,13 +70,17 @@ public class ObjectEnum<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a member.
|
* Registers a member if its not present.
|
||||||
* @param instance - member instance.
|
* @param instance - member instance.
|
||||||
* @param name - name of member.
|
* @param name - name of member.
|
||||||
|
* @return TRUE if the member was registered, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
protected void registerMember(T instance, String name) {
|
public boolean registerMember(T instance, String name) {
|
||||||
members.put(instance, name);
|
if (!members.containsKey(instance)) {
|
||||||
|
members.put(instance, name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,4 +117,9 @@ public class ObjectEnum<T> {
|
|||||||
public Set<T> values() {
|
public Set<T> values() {
|
||||||
return new HashSet<T>(members.keySet());
|
return new HashSet<T>(members.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return members.keySet().iterator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -581,7 +581,7 @@ public class StructureModifier<TField> {
|
|||||||
List<Field> result = new ArrayList<Field>();
|
List<Field> result = new ArrayList<Field>();
|
||||||
|
|
||||||
// Retrieve every private and public field
|
// Retrieve every private and public field
|
||||||
for (Field field : FuzzyReflection.fromClass(type, true).getFields()) {
|
for (Field field : FuzzyReflection.fromClass(type, true).getDeclaredFields(superclassExclude)) {
|
||||||
int mod = field.getModifiers();
|
int mod = field.getModifiers();
|
||||||
|
|
||||||
// Ignore static and "abstract packet" fields
|
// Ignore static and "abstract packet" fields
|
||||||
@ -595,6 +595,4 @@ public class StructureModifier<TField> {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -256,7 +256,7 @@ public class AggregateCloner implements Cloner {
|
|||||||
if (index < cloners.size()) {
|
if (index < cloners.size()) {
|
||||||
return cloners.get(index).clone(source);
|
return cloners.get(index).clone(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Damn - failure
|
// Damn - failure
|
||||||
throw new IllegalArgumentException("Cannot clone " + source + ": No cloner is sutable.");
|
throw new IllegalArgumentException("Cannot clone " + source + ": No cloner is sutable.");
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ public class ImmutableDetector implements Cloner {
|
|||||||
if (Primitives.isWrapperType(type) || String.class.equals(type))
|
if (Primitives.isWrapperType(type) || String.class.equals(type))
|
||||||
return true;
|
return true;
|
||||||
// May not be true, but if so, that kind of code is broken anyways
|
// May not be true, but if so, that kind of code is broken anyways
|
||||||
if (type.isEnum())
|
if (isEnumWorkaround(type))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (Class<?> clazz : immutableClasses)
|
for (Class<?> clazz : immutableClasses)
|
||||||
@ -83,6 +83,16 @@ public class ImmutableDetector implements Cloner {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is just great. Just great.
|
||||||
|
private static boolean isEnumWorkaround(Class<?> enumClass) {
|
||||||
|
while (enumClass != null) {
|
||||||
|
if (enumClass.isEnum())
|
||||||
|
return true;
|
||||||
|
enumClass = enumClass.getSuperclass();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object clone(Object source) {
|
public Object clone(Object source) {
|
||||||
// Safe if the class is immutable
|
// Safe if the class is immutable
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection.FieldAccessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the content of well-known fields in Minecraft.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class MinecraftFields {
|
||||||
|
// Cached accessors
|
||||||
|
private static volatile FieldAccessor CONNECTION_ACCESSOR;
|
||||||
|
private static volatile FieldAccessor NETWORK_ACCESSOR;
|
||||||
|
|
||||||
|
private MinecraftFields() {
|
||||||
|
// Not constructable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the network mananger associated with a particular player.
|
||||||
|
* @param player - the player.
|
||||||
|
* @return The network manager.
|
||||||
|
*/
|
||||||
|
public static Object getNetworkManager(Player player) {
|
||||||
|
Object nmsPlayer = BukkitUnwrapper.getInstance().unwrapItem(player);
|
||||||
|
|
||||||
|
if (NETWORK_ACCESSOR == null) {
|
||||||
|
Class<?> networkClass = MinecraftReflection.getNetworkManagerClass();
|
||||||
|
Class<?> connectionClass = MinecraftReflection.getNetServerHandlerClass();
|
||||||
|
NETWORK_ACCESSOR = FuzzyReflection.getFieldAccessor(connectionClass, networkClass, true);
|
||||||
|
}
|
||||||
|
// Retrieve the network manager
|
||||||
|
return NETWORK_ACCESSOR.get(getPlayerConnection(nmsPlayer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the player connection (or NetServerHandler) associated with a player.
|
||||||
|
* @param player - the player.
|
||||||
|
* @return The player connection.
|
||||||
|
*/
|
||||||
|
public static Object getPlayerConnection(Player player) {
|
||||||
|
return getPlayerConnection(BukkitUnwrapper.getInstance().unwrapItem(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve player connection from a native instance
|
||||||
|
private static Object getPlayerConnection(Object nmsPlayer) {
|
||||||
|
if (CONNECTION_ACCESSOR == null) {
|
||||||
|
Class<?> connectionClass = MinecraftReflection.getNetServerHandlerClass();
|
||||||
|
CONNECTION_ACCESSOR = FuzzyReflection.getFieldAccessor(nmsPlayer.getClass(), connectionClass, true);
|
||||||
|
}
|
||||||
|
return CONNECTION_ACCESSOR.get(nmsPlayer);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,24 @@
|
|||||||
package com.comphenix.protocol.utility;
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.bukkit.command.defaults.EnchantCommand;
|
||||||
|
|
||||||
|
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.util.io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
|
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
||||||
|
import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener;
|
||||||
|
import net.sf.cglib.proxy.Enhancer;
|
||||||
|
import net.sf.cglib.proxy.MethodInterceptor;
|
||||||
|
import net.sf.cglib.proxy.MethodProxy;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,6 +30,14 @@ public class MinecraftMethods {
|
|||||||
// For player connection
|
// For player connection
|
||||||
private volatile static Method sendPacketMethod;
|
private volatile static Method sendPacketMethod;
|
||||||
|
|
||||||
|
// For network manager
|
||||||
|
private volatile static Method networkManagerHandle;
|
||||||
|
private volatile static Method networkManagerPacketRead;
|
||||||
|
|
||||||
|
// For packet
|
||||||
|
private volatile static Method packetReadByteBuf;
|
||||||
|
private volatile static Method packetWriteByteBuf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the send packet method in PlayerConnection/NetServerHandler.
|
* Retrieve the send packet method in PlayerConnection/NetServerHandler.
|
||||||
* @return The send packet method.
|
* @return The send packet method.
|
||||||
@ -47,6 +71,50 @@ public class MinecraftMethods {
|
|||||||
return sendPacketMethod;
|
return sendPacketMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the disconnect method for a given player connection.
|
||||||
|
* @param playerConnection - the player connection.
|
||||||
|
* @return The
|
||||||
|
*/
|
||||||
|
public static Method getDisconnectMethod(Class<? extends Object> playerConnection) {
|
||||||
|
try {
|
||||||
|
return FuzzyReflection.fromClass(playerConnection).getMethodByName("disconnect.*");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Just assume it's the first String method
|
||||||
|
return FuzzyReflection.fromObject(playerConnection).getMethodByParameters("disconnect", String.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the handle(Packet, GenericFutureListener[]) method of network manager.
|
||||||
|
* <p>
|
||||||
|
* This only exists in version 1.7.2 and above.
|
||||||
|
* @return The handle method.
|
||||||
|
*/
|
||||||
|
public static Method getNetworkManagerHandleMethod() {
|
||||||
|
if (networkManagerHandle == null) {
|
||||||
|
networkManagerHandle = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass(), true).
|
||||||
|
getMethodByParameters("handle", MinecraftReflection.getPacketClass(), GenericFutureListener[].class);
|
||||||
|
networkManagerHandle.setAccessible(true);
|
||||||
|
}
|
||||||
|
return networkManagerHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the packetRead(ChannelHandlerContext, Packet) method of NetworkMananger.
|
||||||
|
* <p>
|
||||||
|
* This only exists in version 1.7.2 and above.
|
||||||
|
* @return The packetRead method.
|
||||||
|
*/
|
||||||
|
public static Method getNetworkManagerReadPacketMethod() {
|
||||||
|
if (networkManagerPacketRead == null) {
|
||||||
|
networkManagerPacketRead = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass(), true).
|
||||||
|
getMethodByParameters("packetRead", ChannelHandlerContext.class, MinecraftReflection.getPacketClass());
|
||||||
|
networkManagerPacketRead.setAccessible(true);
|
||||||
|
}
|
||||||
|
return networkManagerPacketRead;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a method mapped list of every method with the given signature.
|
* Retrieve a method mapped list of every method with the given signature.
|
||||||
* @param source - class source.
|
* @param source - class source.
|
||||||
@ -60,5 +128,105 @@ public class MinecraftMethods {
|
|||||||
reflect.getMethodListByParameters(Void.TYPE, params)
|
reflect.getMethodListByParameters(Void.TYPE, params)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Packet.read(PacketDataSerializer) method.
|
||||||
|
* <p>
|
||||||
|
* This only exists in version 1.7.2 and above.
|
||||||
|
* @return The packet read method.
|
||||||
|
*/
|
||||||
|
public static Method getPacketReadByteBufMethod() {
|
||||||
|
initializePacket();
|
||||||
|
return packetReadByteBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Packet.write(PacketDataSerializer) method.
|
||||||
|
* <p>
|
||||||
|
* This only exists in version 1.7.2 and above.
|
||||||
|
* @return The packet write method.
|
||||||
|
*/
|
||||||
|
public static Method getPacketWriteByteBufMethod() {
|
||||||
|
initializePacket();
|
||||||
|
return packetWriteByteBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the two read() and write() methods.
|
||||||
|
*/
|
||||||
|
private static void initializePacket() {
|
||||||
|
// Initialize the methods
|
||||||
|
if (packetReadByteBuf == null || packetWriteByteBuf == null) {
|
||||||
|
// This object will allow us to detect which methods were called
|
||||||
|
Enhancer enhancer = new Enhancer();
|
||||||
|
enhancer.setSuperclass(MinecraftReflection.getPacketDataSerializerClass());
|
||||||
|
enhancer.setCallback(new MethodInterceptor() {
|
||||||
|
@Override
|
||||||
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
|
||||||
|
throws Throwable {
|
||||||
|
if (method.getName().contains("read"))
|
||||||
|
throw new ReadMethodException();
|
||||||
|
if (method.getName().contains("write"))
|
||||||
|
throw new WriteMethodException();
|
||||||
|
return proxy.invokeSuper(obj, args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create our proxy object
|
||||||
|
Object javaProxy = enhancer.create(
|
||||||
|
new Class<?>[] { ByteBuf.class },
|
||||||
|
new Object[] { UnpooledByteBufAllocator.DEFAULT.buffer() }
|
||||||
|
);
|
||||||
|
|
||||||
|
Object lookPacket = new PacketContainer(PacketType.Play.Client.PLACE).getHandle();
|
||||||
|
List<Method> candidates = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
|
||||||
|
getMethodListByParameters(Void.TYPE, new Class<?>[] { MinecraftReflection.getPacketDataSerializerClass() });
|
||||||
|
|
||||||
|
// Look through all the methods
|
||||||
|
for (Method method : candidates) {
|
||||||
|
try {
|
||||||
|
method.invoke(lookPacket, javaProxy);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
if (e.getCause() instanceof ReadMethodException)
|
||||||
|
// Must be the reader
|
||||||
|
packetReadByteBuf = method;
|
||||||
|
else if (e.getCause() instanceof WriteMethodException)
|
||||||
|
packetWriteByteBuf = method;
|
||||||
|
else
|
||||||
|
throw new RuntimeException("Inner exception.", e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Generic reflection error.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packetReadByteBuf == null)
|
||||||
|
throw new IllegalStateException("Unable to find Packet.read(PacketDataSerializer)");
|
||||||
|
if (packetWriteByteBuf == null)
|
||||||
|
throw new IllegalStateException("Unable to find Packet.write(PacketDataSerializer)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An internal exception used to detect read methods.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
private static class ReadMethodException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public ReadMethodException() {
|
||||||
|
super("A read method was executed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An internal exception used to detect write methods.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
private static class WriteMethodException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public WriteMethodException() {
|
||||||
|
super("A write method was executed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
||||||
import net.sf.cglib.asm.ClassReader;
|
import net.sf.cglib.asm.ClassReader;
|
||||||
import net.sf.cglib.asm.MethodVisitor;
|
import net.sf.cglib.asm.MethodVisitor;
|
||||||
import net.sf.cglib.asm.Opcodes;
|
import net.sf.cglib.asm.Opcodes;
|
||||||
@ -43,7 +44,7 @@ import org.bukkit.Bukkit;
|
|||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.ProtocolLibrary;
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
import com.comphenix.protocol.error.ErrorReporter;
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
import com.comphenix.protocol.error.Report;
|
import com.comphenix.protocol.error.Report;
|
||||||
@ -118,7 +119,8 @@ public class MinecraftReflection {
|
|||||||
|
|
||||||
// New in 1.4.5
|
// New in 1.4.5
|
||||||
private static Method craftNMSMethod;
|
private static Method craftNMSMethod;
|
||||||
private static Method craftBukkitMethod;
|
private static Method craftBukkitNMS;
|
||||||
|
private static Method craftBukkitOBC;
|
||||||
private static boolean craftItemStackFailed;
|
private static boolean craftItemStackFailed;
|
||||||
|
|
||||||
// The NMS version
|
// The NMS version
|
||||||
@ -135,6 +137,9 @@ public class MinecraftReflection {
|
|||||||
*/
|
*/
|
||||||
private static boolean initializing;
|
private static boolean initializing;
|
||||||
|
|
||||||
|
// Whether or not we are using netty
|
||||||
|
private static Boolean cachedNetty;
|
||||||
|
|
||||||
private MinecraftReflection() {
|
private MinecraftReflection() {
|
||||||
// No need to make this constructable.
|
// No need to make this constructable.
|
||||||
}
|
}
|
||||||
@ -602,6 +607,23 @@ public class MinecraftReflection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this Minecraft version is using Netty.
|
||||||
|
* <p>
|
||||||
|
* Spigot is ignored in this consideration.
|
||||||
|
* @return TRUE if it does, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean isUsingNetty() {
|
||||||
|
if (cachedNetty == null) {
|
||||||
|
try {
|
||||||
|
cachedNetty = getEnumProtocolClass() != null;
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
cachedNetty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cachedNetty;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the least derived class, except Object.
|
* Retrieve the least derived class, except Object.
|
||||||
* @return Least derived super class.
|
* @return Least derived super class.
|
||||||
@ -1045,7 +1067,7 @@ public class MinecraftReflection {
|
|||||||
try {
|
try {
|
||||||
return getMinecraftClass("AttributeSnapshot");
|
return getMinecraftClass("AttributeSnapshot");
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
final Class<?> packetUpdateAttributes = PacketRegistry.getPacketClassFromID(44, true);
|
final Class<?> packetUpdateAttributes = PacketRegistry.getPacketClassFromType(PacketType.Play.Server.UPDATE_ATTRIBUTES, true);
|
||||||
final String packetSignature = packetUpdateAttributes.getCanonicalName().replace('.', '/');
|
final String packetSignature = packetUpdateAttributes.getCanonicalName().replace('.', '/');
|
||||||
|
|
||||||
// HACK - class is found by inspecting code
|
// HACK - class is found by inspecting code
|
||||||
@ -1147,7 +1169,7 @@ public class MinecraftReflection {
|
|||||||
return getMinecraftClass("MobEffect");
|
return getMinecraftClass("MobEffect");
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
// It is the second parameter in Packet41MobEffect
|
// It is the second parameter in Packet41MobEffect
|
||||||
Class<?> packet = PacketRegistry.getPacketClassFromID(Packets.Server.MOB_EFFECT);
|
Class<?> packet = PacketRegistry.getPacketClassFromType(PacketType.Play.Server.MOB_EFFECT);
|
||||||
Constructor<?> constructor = FuzzyReflection.fromClass(packet).getConstructor(
|
Constructor<?> constructor = FuzzyReflection.fromClass(packet).getConstructor(
|
||||||
FuzzyMethodContract.newBuilder().
|
FuzzyMethodContract.newBuilder().
|
||||||
parameterCount(2).
|
parameterCount(2).
|
||||||
@ -1159,6 +1181,41 @@ public class MinecraftReflection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the packet data serializer class that overrides ByteBuf.
|
||||||
|
* @return The data serializer class.
|
||||||
|
*/
|
||||||
|
public static Class<?> getPacketDataSerializerClass() {
|
||||||
|
try {
|
||||||
|
return getMinecraftClass("PacketDataSerializer");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
Class<?> packet = getPacketClass();
|
||||||
|
Method method = FuzzyReflection.fromClass(packet).getMethod(
|
||||||
|
FuzzyMethodContract.newBuilder().
|
||||||
|
parameterCount(1).
|
||||||
|
parameterDerivedOf(ByteBuf.class).
|
||||||
|
returnTypeVoid().
|
||||||
|
build()
|
||||||
|
);
|
||||||
|
return setMinecraftClass("PacketDataSerializer", method.getParameterTypes()[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an instance of the packet data serializer wrapper.
|
||||||
|
* @param buffer - the buffer.
|
||||||
|
* @return The instance.
|
||||||
|
*/
|
||||||
|
public static ByteBuf getPacketDataSerializer(ByteBuf buffer) {
|
||||||
|
Class<?> packetSerializer = getPacketDataSerializerClass();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (ByteBuf) packetSerializer.getConstructor(ByteBuf.class).newInstance(buffer);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Cannot construct packet serializer.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if a given method retrieved by ASM is a constructor.
|
* Determine if a given method retrieved by ASM is a constructor.
|
||||||
* @param name - the name of the method.
|
* @param name - the name of the method.
|
||||||
@ -1219,7 +1276,7 @@ public class MinecraftReflection {
|
|||||||
*/
|
*/
|
||||||
public static ItemStack getBukkitItemStack(ItemStack bukkitItemStack) {
|
public static ItemStack getBukkitItemStack(ItemStack bukkitItemStack) {
|
||||||
// Delegate this task to the method that can execute it
|
// Delegate this task to the method that can execute it
|
||||||
if (craftBukkitMethod != null)
|
if (craftBukkitNMS != null)
|
||||||
return getBukkitItemByMethod(bukkitItemStack);
|
return getBukkitItemByMethod(bukkitItemStack);
|
||||||
|
|
||||||
if (craftBukkitConstructor == null) {
|
if (craftBukkitConstructor == null) {
|
||||||
@ -1243,9 +1300,10 @@ public class MinecraftReflection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ItemStack getBukkitItemByMethod(ItemStack bukkitItemStack) {
|
private static ItemStack getBukkitItemByMethod(ItemStack bukkitItemStack) {
|
||||||
if (craftBukkitMethod == null) {
|
if (craftBukkitNMS == null) {
|
||||||
try {
|
try {
|
||||||
craftBukkitMethod = getCraftItemStackClass().getMethod("asCraftCopy", ItemStack.class);
|
craftBukkitNMS = getCraftItemStackClass().getMethod("asNMSCopy", ItemStack.class);
|
||||||
|
craftBukkitOBC = getCraftItemStackClass().getMethod("asCraftMirror", MinecraftReflection.getItemStackClass());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
craftItemStackFailed = true;
|
craftItemStackFailed = true;
|
||||||
throw new RuntimeException("Cannot find CraftItemStack.asCraftCopy(org.bukkit.inventory.ItemStack).", e);
|
throw new RuntimeException("Cannot find CraftItemStack.asCraftCopy(org.bukkit.inventory.ItemStack).", e);
|
||||||
@ -1254,7 +1312,8 @@ public class MinecraftReflection {
|
|||||||
|
|
||||||
// Next, construct it
|
// Next, construct it
|
||||||
try {
|
try {
|
||||||
return (ItemStack) craftBukkitMethod.invoke(null, bukkitItemStack);
|
Object nmsItemStack = craftBukkitNMS.invoke(null, bukkitItemStack);
|
||||||
|
return (ItemStack) craftBukkitOBC.invoke(null, nmsItemStack);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Cannot construct CraftItemStack.", e);
|
throw new RuntimeException("Cannot construct CraftItemStack.", e);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.comphenix.protocol.utility;
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@ -34,7 +35,9 @@ import com.google.common.collect.Ordering;
|
|||||||
*
|
*
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class MinecraftVersion implements Comparable<MinecraftVersion> {
|
public class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regular expression used to parse version strings.
|
* Regular expression used to parse version strings.
|
||||||
*/
|
*/
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package com.comphenix.protocol.utility;
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@ -14,12 +16,16 @@ import com.google.common.collect.ComparisonChain;
|
|||||||
* Used to parse a snapshot version.
|
* Used to parse a snapshot version.
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class SnapshotVersion implements Comparable<SnapshotVersion> {
|
public class SnapshotVersion implements Comparable<SnapshotVersion>, Serializable {
|
||||||
|
// Increment when the class changes
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("(\\d{2}w\\d{2})([a-z])");
|
private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("(\\d{2}w\\d{2})([a-z])");
|
||||||
|
|
||||||
private final String rawString;
|
|
||||||
private final Date snapshotDate;
|
private final Date snapshotDate;
|
||||||
private final int snapshotWeekVersion;
|
private final int snapshotWeekVersion;
|
||||||
|
|
||||||
|
private transient String rawString;
|
||||||
|
|
||||||
public SnapshotVersion(String version) {
|
public SnapshotVersion(String version) {
|
||||||
Matcher matcher = SNAPSHOT_PATTERN.matcher(version.trim());
|
Matcher matcher = SNAPSHOT_PATTERN.matcher(version.trim());
|
||||||
@ -70,6 +76,15 @@ public class SnapshotVersion implements Comparable<SnapshotVersion> {
|
|||||||
* @return The snapshot string.
|
* @return The snapshot string.
|
||||||
*/
|
*/
|
||||||
public String getSnapshotString() {
|
public String getSnapshotString() {
|
||||||
|
if (rawString == null) {
|
||||||
|
// It's essential that we use the same locale
|
||||||
|
Calendar current = Calendar.getInstance(Locale.US);
|
||||||
|
current.setTime(snapshotDate);
|
||||||
|
rawString = String.format("%02dw%02d%s",
|
||||||
|
current.get(Calendar.YEAR) % 100,
|
||||||
|
current.get(Calendar.WEEK_OF_YEAR),
|
||||||
|
(char) ((int)'a' + snapshotWeekVersion));
|
||||||
|
}
|
||||||
return rawString;
|
return rawString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +118,6 @@ public class SnapshotVersion implements Comparable<SnapshotVersion> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return rawString;
|
return getSnapshotString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,8 @@ public class ChunkPosition {
|
|||||||
|
|
||||||
// Construct the underlying ChunkPosition
|
// Construct the underlying ChunkPosition
|
||||||
try {
|
try {
|
||||||
return chunkPositionConstructor.newInstance(specific.x, specific.y, specific.z);
|
Object result = chunkPositionConstructor.newInstance(specific.x, specific.y, specific.z);
|
||||||
|
return result;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Cannot construct ChunkPosition.", e);
|
throw new RuntimeException("Cannot construct ChunkPosition.", e);
|
||||||
}
|
}
|
||||||
@ -183,7 +184,8 @@ public class ChunkPosition {
|
|||||||
if (intModifier.size() >= 3) {
|
if (intModifier.size() >= 3) {
|
||||||
try {
|
try {
|
||||||
StructureModifier<Integer> instance = intModifier.withTarget(generic);
|
StructureModifier<Integer> instance = intModifier.withTarget(generic);
|
||||||
return new ChunkPosition(instance.read(0), instance.read(1), instance.read(2));
|
ChunkPosition result = new ChunkPosition(instance.read(0), instance.read(1), instance.read(2));
|
||||||
|
return result;
|
||||||
} catch (FieldAccessException e) {
|
} catch (FieldAccessException e) {
|
||||||
// This is an exeptional work-around, so we don't want to burden the caller with the messy details
|
// This is an exeptional work-around, so we don't want to burden the caller with the messy details
|
||||||
throw new RuntimeException("Field access error.", e);
|
throw new RuntimeException("Field access error.", e);
|
||||||
@ -224,6 +226,6 @@ public class ChunkPosition {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ChunkPosition [x=" + x + ", y=" + y + ", z=" + z + "]";
|
return "WrappedChunkPosition [x=" + x + ", y=" + y + ", z=" + z + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.comphenix.protocol.wrappers;
|
package com.comphenix.protocol.wrappers;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -52,7 +53,6 @@ import com.google.common.collect.Iterators;
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class WrappedDataWatcher implements Iterable<WrappedWatchableObject> {
|
public class WrappedDataWatcher implements Iterable<WrappedWatchableObject> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to assign integer IDs to given types.
|
* Used to assign integer IDs to given types.
|
||||||
*/
|
*/
|
||||||
@ -61,12 +61,16 @@ public class WrappedDataWatcher implements Iterable<WrappedWatchableObject> {
|
|||||||
// Fields
|
// Fields
|
||||||
private static Field valueMapField;
|
private static Field valueMapField;
|
||||||
private static Field readWriteLockField;
|
private static Field readWriteLockField;
|
||||||
|
private static Field entityField;
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
private static Method createKeyValueMethod;
|
private static Method createKeyValueMethod;
|
||||||
private static Method updateKeyValueMethod;
|
private static Method updateKeyValueMethod;
|
||||||
private static Method getKeyValueMethod;
|
private static Method getKeyValueMethod;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
private static Constructor<?> createDataWatcherConstructor;
|
||||||
|
|
||||||
// Entity methods
|
// Entity methods
|
||||||
private volatile static Field entityDataField;
|
private volatile static Field entityDataField;
|
||||||
|
|
||||||
@ -94,7 +98,11 @@ public class WrappedDataWatcher implements Iterable<WrappedWatchableObject> {
|
|||||||
public WrappedDataWatcher() {
|
public WrappedDataWatcher() {
|
||||||
// Just create a new watcher
|
// Just create a new watcher
|
||||||
try {
|
try {
|
||||||
this.handle = MinecraftReflection.getDataWatcherClass().newInstance();
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
|
this.handle = newEntityHandle(null);
|
||||||
|
} else {
|
||||||
|
this.handle = MinecraftReflection.getDataWatcherClass().newInstance();
|
||||||
|
}
|
||||||
initialize();
|
initialize();
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -117,6 +125,42 @@ public class WrappedDataWatcher implements Iterable<WrappedWatchableObject> {
|
|||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new data watcher with the given entity.
|
||||||
|
* <p>
|
||||||
|
* In 1.6.4 and ealier, this will fall back to using {@link #WrappedDataWatcher()}.
|
||||||
|
* @param entity - the entity.
|
||||||
|
* @return The wrapped data watcher.
|
||||||
|
*/
|
||||||
|
public static WrappedDataWatcher newWithEntity(Entity entity) {
|
||||||
|
// Use the old constructor
|
||||||
|
if (!MinecraftReflection.isUsingNetty())
|
||||||
|
return new WrappedDataWatcher();
|
||||||
|
return new WrappedDataWatcher(newEntityHandle(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new native DataWatcher with the given entity.
|
||||||
|
* <p>
|
||||||
|
* Warning: This is only supported in 1.7.2 and above.
|
||||||
|
* @param entity - the entity, or NULL.
|
||||||
|
* @return The data watcher.
|
||||||
|
*/
|
||||||
|
private static Object newEntityHandle(Entity entity) {
|
||||||
|
Class<?> dataWatcher = MinecraftReflection.getDataWatcherClass();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (createDataWatcherConstructor == null)
|
||||||
|
createDataWatcherConstructor = dataWatcher.getConstructor(MinecraftReflection.getEntityClass());
|
||||||
|
|
||||||
|
return createDataWatcherConstructor.newInstance(
|
||||||
|
BukkitUnwrapper.getInstance().unwrapItem(entity)
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Cannot construct data watcher.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new data watcher for a list of watchable objects.
|
* Create a new data watcher for a list of watchable objects.
|
||||||
* <p>
|
* <p>
|
||||||
@ -548,6 +592,11 @@ public class WrappedDataWatcher implements Iterable<WrappedWatchableObject> {
|
|||||||
// It's not a big deal
|
// It's not a big deal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for the entity field as well
|
||||||
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
|
entityField = fuzzy.getFieldByType("entity", MinecraftReflection.getEntityClass());
|
||||||
|
entityField.setAccessible(true);
|
||||||
|
}
|
||||||
initializeMethods(fuzzy);
|
initializeMethods(fuzzy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,4 +697,38 @@ public class WrappedDataWatcher implements Iterable<WrappedWatchableObject> {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return asMap().toString();
|
return asMap().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the entity associated with this data watcher.
|
||||||
|
* <p>
|
||||||
|
* <b>Warning:</b> This is only supported on 1.7.2 and above.
|
||||||
|
* @return The entity, or NULL.
|
||||||
|
*/
|
||||||
|
public Entity getEntity() {
|
||||||
|
if (!MinecraftReflection.isUsingNetty())
|
||||||
|
throw new IllegalStateException("This method is only supported on 1.7.2 and above.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (Entity) MinecraftReflection.getBukkitEntity(entityField.get(handle));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Unable to retrieve entity.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the entity associated with this data watcher.
|
||||||
|
* <p>
|
||||||
|
* <b>Warning:</b> This is only supported on 1.7.2 and above.
|
||||||
|
* @param entity - the new entity.
|
||||||
|
*/
|
||||||
|
public void setEntity(Entity entity) {
|
||||||
|
if (!MinecraftReflection.isUsingNetty())
|
||||||
|
throw new IllegalStateException("This method is only supported on 1.7.2 and above.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
entityField.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Unable to set entity.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,10 @@ import java.util.Iterator;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a map that wraps another map by transforming the entries going in and out.
|
* Represents a map that wraps another map by transforming the entries going in and out.
|
||||||
@ -95,12 +99,13 @@ public abstract class ConvertedMap<Key, VInner, VOuter> extends AbstractConverte
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VOuter getValue() {
|
public VOuter getValue() {
|
||||||
return ConvertedMap.this.toOuter(inner.getValue());
|
return ConvertedMap.this.toOuter(inner.getKey(), inner.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VOuter setValue(VOuter value) {
|
public VOuter setValue(VOuter value) {
|
||||||
return ConvertedMap.this.toOuter(inner.setValue(ConvertedMap.this.toInner(value)));
|
final VInner converted = ConvertedMap.this.toInner(getKey(), value);
|
||||||
|
return ConvertedMap.this.toOuter(getKey(), inner.setValue(converted));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -112,9 +117,28 @@ public abstract class ConvertedMap<Key, VInner, VOuter> extends AbstractConverte
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a value from the inner map to the outer visible map.
|
||||||
|
* @param inner - the inner value.
|
||||||
|
* @return The outer value.
|
||||||
|
*/
|
||||||
|
protected VOuter toOuter(Key key, VInner inner) {
|
||||||
|
return toOuter(inner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a value from the outer map to the internal inner map.
|
||||||
|
* @param outer - the outer value.
|
||||||
|
* @return The inner value.
|
||||||
|
*/
|
||||||
|
protected VInner toInner(Key key, VOuter outer) {
|
||||||
|
return toInner(outer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public VOuter get(Object key) {
|
public VOuter get(Object key) {
|
||||||
return toOuter(inner.get(key));
|
return toOuter((Key) key, inner.get(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -129,7 +153,7 @@ public abstract class ConvertedMap<Key, VInner, VOuter> extends AbstractConverte
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VOuter put(Key key, VOuter value) {
|
public VOuter put(Key key, VOuter value) {
|
||||||
return toOuter(inner.put(key, toInner(value)));
|
return toOuter(key, inner.put(key, toInner(key, value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -139,9 +163,10 @@ public abstract class ConvertedMap<Key, VInner, VOuter> extends AbstractConverte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public VOuter remove(Object key) {
|
public VOuter remove(Object key) {
|
||||||
return toOuter(inner.remove(key));
|
return toOuter((Key) key, inner.remove(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -151,17 +176,12 @@ public abstract class ConvertedMap<Key, VInner, VOuter> extends AbstractConverte
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<VOuter> values() {
|
public Collection<VOuter> values() {
|
||||||
return new ConvertedCollection<VInner, VOuter>(inner.values()) {
|
return Collections2.transform(entrySet(), new Function<Entry<Key, VOuter>, VOuter>() {
|
||||||
@Override
|
@Override
|
||||||
protected VOuter toOuter(VInner inner) {
|
public VOuter apply(@Nullable java.util.Map.Entry<Key, VOuter> entry) {
|
||||||
return ConvertedMap.this.toOuter(inner);
|
return entry.getValue();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
@Override
|
|
||||||
protected VInner toInner(VOuter outer) {
|
|
||||||
return ConvertedMap.this.toInner(outer);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
package com.comphenix.protocol.wrappers.nbt;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.minecraft.util.com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
|
||||||
|
public abstract class NameProperty {
|
||||||
|
private static final Map<Class<?>, StructureModifier<String>> MODIFIERS = Maps.newConcurrentMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the name.
|
||||||
|
* @return The name.
|
||||||
|
*/
|
||||||
|
public abstract String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name.
|
||||||
|
* @param name - the new value of the name.
|
||||||
|
*/
|
||||||
|
public abstract void setName(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the string modifier for a particular class.
|
||||||
|
* @param baseClass - the base class.
|
||||||
|
* @return The string modifier, with no target.
|
||||||
|
*/
|
||||||
|
private static StructureModifier<String> getModifier(Class<?> baseClass) {
|
||||||
|
StructureModifier<String> modifier = MODIFIERS.get(baseClass);
|
||||||
|
|
||||||
|
// Share modifier
|
||||||
|
if (modifier == null) {
|
||||||
|
modifier = new StructureModifier<Object>(baseClass, Object.class, false).withType(String.class);
|
||||||
|
MODIFIERS.put(baseClass, modifier);
|
||||||
|
}
|
||||||
|
return modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a string of the given index exists in the base class.
|
||||||
|
* @param baseClass - the base class.
|
||||||
|
* @param index - the index to check.
|
||||||
|
* @return TRUE if it does, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean hasStringIndex(Class<?> baseClass, int index) {
|
||||||
|
if (index < 0)
|
||||||
|
return false;
|
||||||
|
return index < getModifier(baseClass).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a name property that delegates all read and write operations to a field of the given target.
|
||||||
|
* @param baseClass - the base class.
|
||||||
|
* @param target - the target
|
||||||
|
* @param index - the index of the field.
|
||||||
|
* @return The name property.
|
||||||
|
*/
|
||||||
|
public static NameProperty fromStringIndex(Class<?> baseClass, Object target, final int index) {
|
||||||
|
final StructureModifier<String> modifier = getModifier(baseClass).withTarget(target);
|
||||||
|
|
||||||
|
return new NameProperty() {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return modifier.read(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setName(String name) {
|
||||||
|
modifier.write(index, name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a new name property around a simple field, forming a Java bean.
|
||||||
|
* @return The name property.
|
||||||
|
*/
|
||||||
|
public static NameProperty fromBean() {
|
||||||
|
return new NameProperty() {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -41,6 +41,7 @@ import com.comphenix.protocol.wrappers.BukkitConverters;
|
|||||||
public class NbtFactory {
|
public class NbtFactory {
|
||||||
// Used to create the underlying tag
|
// Used to create the underlying tag
|
||||||
private static Method methodCreateTag;
|
private static Method methodCreateTag;
|
||||||
|
private static boolean methodCreateWithName;
|
||||||
|
|
||||||
// Item stack trickery
|
// Item stack trickery
|
||||||
private static StructureModifier<Object> itemStackModifier;
|
private static StructureModifier<Object> itemStackModifier;
|
||||||
@ -188,10 +189,13 @@ public class NbtFactory {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a NBT wrapper.
|
* Initialize a NBT wrapper.
|
||||||
|
* <p>
|
||||||
|
* Use {@link #fromNMS(Object, String)} instead.
|
||||||
* @param handle - the underlying net.minecraft.server object to wrap.
|
* @param handle - the underlying net.minecraft.server object to wrap.
|
||||||
* @return A NBT wrapper.
|
* @return A NBT wrapper.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
@Deprecated
|
||||||
public static <T> NbtWrapper<T> fromNMS(Object handle) {
|
public static <T> NbtWrapper<T> fromNMS(Object handle) {
|
||||||
WrappedElement<T> partial = new WrappedElement<T>(handle);
|
WrappedElement<T> partial = new WrappedElement<T>(handle);
|
||||||
|
|
||||||
@ -203,6 +207,25 @@ public class NbtFactory {
|
|||||||
else
|
else
|
||||||
return partial;
|
return partial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a NBT wrapper with a name.
|
||||||
|
* @param name - the name of the tag, or NULL if not valid.
|
||||||
|
* @param handle - the underlying net.minecraft.server object to wrap.
|
||||||
|
* @return A NBT wrapper.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
public static <T> NbtWrapper<T> fromNMS(Object handle, String name) {
|
||||||
|
WrappedElement<T> partial = new WrappedElement<T>(handle, name);
|
||||||
|
|
||||||
|
// See if this is actually a compound tag
|
||||||
|
if (partial.getType() == NbtType.TAG_COMPOUND)
|
||||||
|
return (NbtWrapper<T>) new WrappedCompound(handle, name);
|
||||||
|
else if (partial.getType() == NbtType.TAG_LIST)
|
||||||
|
return new WrappedList(handle, name);
|
||||||
|
else
|
||||||
|
return partial;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the NBT compound from a given NMS handle.
|
* Retrieve the NBT compound from a given NMS handle.
|
||||||
@ -351,7 +374,6 @@ public class NbtFactory {
|
|||||||
* @return The new wrapped NBT tag.
|
* @return The new wrapped NBT tag.
|
||||||
* @throws FieldAccessException If we're unable to create the underlying tag.
|
* @throws FieldAccessException If we're unable to create the underlying tag.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
||||||
public static <T> NbtWrapper<T> ofWrapper(NbtType type, String name) {
|
public static <T> NbtWrapper<T> ofWrapper(NbtType type, String name) {
|
||||||
if (type == null)
|
if (type == null)
|
||||||
throw new IllegalArgumentException("type cannot be NULL.");
|
throw new IllegalArgumentException("type cannot be NULL.");
|
||||||
@ -362,19 +384,22 @@ public class NbtFactory {
|
|||||||
Class<?> base = MinecraftReflection.getNBTBaseClass();
|
Class<?> base = MinecraftReflection.getNBTBaseClass();
|
||||||
|
|
||||||
// Use the base class
|
// Use the base class
|
||||||
methodCreateTag = FuzzyReflection.fromClass(base).
|
try {
|
||||||
getMethodByParameters("createTag", base, new Class<?>[] { byte.class, String.class });
|
methodCreateTag = findCreateMethod(base, byte.class, String.class);
|
||||||
|
methodCreateWithName = true;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
methodCreateTag = findCreateMethod(base, byte.class);
|
||||||
|
methodCreateWithName = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Object handle = methodCreateTag.invoke(null, (byte) type.getRawID(), name);
|
// Delegate to the correct version
|
||||||
|
if (methodCreateWithName)
|
||||||
if (type == NbtType.TAG_COMPOUND)
|
return createTagWithName(type, name);
|
||||||
return (NbtWrapper<T>) new WrappedCompound(handle);
|
|
||||||
else if (type == NbtType.TAG_LIST)
|
|
||||||
return (NbtWrapper<T>) new WrappedList(handle);
|
|
||||||
else
|
else
|
||||||
return new WrappedElement<T>(handle);
|
return createTagSetName(type, name);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Inform the caller
|
// Inform the caller
|
||||||
@ -383,6 +408,43 @@ public class NbtFactory {
|
|||||||
e);
|
e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the create method of NBTBase.
|
||||||
|
* @param base - the base NBT.
|
||||||
|
* @param params - the parameters.
|
||||||
|
*/
|
||||||
|
private static Method findCreateMethod(Class<?> base, Class<?>... params) {
|
||||||
|
Method method = FuzzyReflection.fromClass(base, true).getMethodByParameters("createTag", base, params);
|
||||||
|
method.setAccessible(true);
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Minecraft 1.6.4 and below
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
private static <T> NbtWrapper<T> createTagWithName(NbtType type, String name) throws Exception {
|
||||||
|
Object handle = methodCreateTag.invoke(null, (byte) type.getRawID(), name);
|
||||||
|
|
||||||
|
if (type == NbtType.TAG_COMPOUND)
|
||||||
|
return (NbtWrapper<T>) new WrappedCompound(handle);
|
||||||
|
else if (type == NbtType.TAG_LIST)
|
||||||
|
return (NbtWrapper<T>) new WrappedList(handle);
|
||||||
|
else
|
||||||
|
return new WrappedElement<T>(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Minecraft 1.7.2 and above
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
private static <T> NbtWrapper<T> createTagSetName(NbtType type, String name) throws Exception {
|
||||||
|
Object handle = methodCreateTag.invoke(null, (byte) type.getRawID());
|
||||||
|
|
||||||
|
if (type == NbtType.TAG_COMPOUND)
|
||||||
|
return (NbtWrapper<T>) new WrappedCompound(handle, name);
|
||||||
|
else if (type == NbtType.TAG_LIST)
|
||||||
|
return (NbtWrapper<T>) new WrappedList(handle, name);
|
||||||
|
else
|
||||||
|
return new WrappedElement<T>(handle, name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new NBT wrapper from a given type.
|
* Create a new NBT wrapper from a given type.
|
||||||
|
@ -69,6 +69,15 @@ class WrappedCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterable<N
|
|||||||
public WrappedCompound(Object handle) {
|
public WrappedCompound(Object handle) {
|
||||||
this.container = new WrappedElement<Map<String,Object>>(handle);
|
this.container = new WrappedElement<Map<String,Object>>(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a wrapped compound from a given NMS handle.
|
||||||
|
* @param handle - the NMS handle.
|
||||||
|
* @param name - the name of the current compound.
|
||||||
|
*/
|
||||||
|
public WrappedCompound(Object handle, String name) {
|
||||||
|
this.container = new WrappedElement<Map<String,Object>>(handle, name);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(NbtVisitor visitor) {
|
public boolean accept(NbtVisitor visitor) {
|
||||||
@ -128,17 +137,25 @@ class WrappedCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterable<N
|
|||||||
savedMap = new ConvertedMap<String, Object, NbtBase<?>>(container.getValue()) {
|
savedMap = new ConvertedMap<String, Object, NbtBase<?>>(container.getValue()) {
|
||||||
@Override
|
@Override
|
||||||
protected Object toInner(NbtBase<?> outer) {
|
protected Object toInner(NbtBase<?> outer) {
|
||||||
if (outer == null)
|
if (outer == null)
|
||||||
return null;
|
return null;
|
||||||
return NbtFactory.fromBase(outer).getHandle();
|
return NbtFactory.fromBase(outer).getHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
protected NbtBase<?> toOuter(Object inner) {
|
protected NbtBase<?> toOuter(Object inner) {
|
||||||
if (inner == null)
|
if (inner == null)
|
||||||
return null;
|
return null;
|
||||||
return NbtFactory.fromNMS(inner);
|
return NbtFactory.fromNMS(inner);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NbtBase<?> toOuter(String key, Object inner) {
|
||||||
|
if (inner == null)
|
||||||
|
return null;
|
||||||
|
return NbtFactory.fromNMS(inner, key);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return WrappedCompound.this.toString();
|
return WrappedCompound.this.toString();
|
||||||
|
@ -34,15 +34,14 @@ import com.google.common.base.Objects;
|
|||||||
* @param <TType> - type of the value field.
|
* @param <TType> - type of the value field.
|
||||||
*/
|
*/
|
||||||
class WrappedElement<TType> implements NbtWrapper<TType> {
|
class WrappedElement<TType> implements NbtWrapper<TType> {
|
||||||
// Structure modifier for the base class
|
|
||||||
private static volatile StructureModifier<Object> baseModifier;
|
|
||||||
|
|
||||||
// For retrieving the current type ID
|
// For retrieving the current type ID
|
||||||
private static volatile Method methodGetTypeID;
|
private static volatile Method methodGetTypeID;
|
||||||
|
|
||||||
// For handling cloning
|
// For handling cloning
|
||||||
private static volatile Method methodClone;
|
private static volatile Method methodClone;
|
||||||
|
|
||||||
|
// Which name property to use
|
||||||
|
private static volatile Boolean hasNbtName;
|
||||||
|
|
||||||
// Structure modifiers for the different NBT elements
|
// Structure modifiers for the different NBT elements
|
||||||
private static StructureModifier<?>[] modifiers = new StructureModifier<?>[NbtType.values().length];
|
private static StructureModifier<?>[] modifiers = new StructureModifier<?>[NbtType.values().length];
|
||||||
|
|
||||||
@ -52,27 +51,43 @@ class WrappedElement<TType> implements NbtWrapper<TType> {
|
|||||||
// Saved type
|
// Saved type
|
||||||
private NbtType type;
|
private NbtType type;
|
||||||
|
|
||||||
|
// Saved name
|
||||||
|
private NameProperty nameProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a NBT wrapper for a generic element.
|
* Initialize a NBT wrapper for a generic element.
|
||||||
* @param handle - the NBT element to wrap.
|
* @param handle - the NBT element to wrap.
|
||||||
*/
|
*/
|
||||||
public WrappedElement(Object handle) {
|
public WrappedElement(Object handle) {
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
|
initializeProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the modifier (with no target) that is used to read and write the NBT name.
|
* Initialize a NBT wrapper for a generic element.
|
||||||
* @return A modifier for accessing the NBT name.
|
* @param handle - the NBT element to wrap.
|
||||||
*/
|
*/
|
||||||
protected static StructureModifier<String> getBaseModifier() {
|
public WrappedElement(Object handle, String name) {
|
||||||
if (baseModifier == null) {
|
this.handle = handle;
|
||||||
|
initializeProperty();
|
||||||
|
setName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeProperty() {
|
||||||
|
if (nameProperty == null) {
|
||||||
Class<?> base = MinecraftReflection.getNBTBaseClass();
|
Class<?> base = MinecraftReflection.getNBTBaseClass();
|
||||||
|
|
||||||
// This will be the same for all classes, so we'll share modifier
|
// Determine if we have a NBT string field
|
||||||
baseModifier = new StructureModifier<Object>(base, Object.class, false).withType(String.class);
|
if (hasNbtName == null) {
|
||||||
|
hasNbtName = NameProperty.hasStringIndex(base, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now initialize the name property
|
||||||
|
if (hasNbtName)
|
||||||
|
this.nameProperty = NameProperty.fromStringIndex(base, handle, 0);
|
||||||
|
else
|
||||||
|
this.nameProperty = NameProperty.fromBean();
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseModifier.withType(String.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,12 +174,12 @@ class WrappedElement<TType> implements NbtWrapper<TType> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return getBaseModifier().withTarget(handle).read(0);
|
return nameProperty.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
getBaseModifier().withTarget(handle).write(0, name);
|
nameProperty.setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -194,7 +209,7 @@ class WrappedElement<TType> implements NbtWrapper<TType> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return NbtFactory.fromNMS(methodClone.invoke(handle));
|
return NbtFactory.fromNMS(methodClone.invoke(handle), getName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new FieldAccessException("Unable to clone " + handle, e);
|
throw new FieldAccessException("Unable to clone " + handle, e);
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,16 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, Iterable<T
|
|||||||
this.container = new WrappedElement<List<Object>>(handle);
|
this.container = new WrappedElement<List<Object>>(handle);
|
||||||
this.elementType = container.getSubType();
|
this.elementType = container.getSubType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a list from an NMS instance.
|
||||||
|
* @param handle - NMS instance.
|
||||||
|
* @param name - name of the current list.
|
||||||
|
*/
|
||||||
|
public WrappedList(Object handle, String name) {
|
||||||
|
this.container = new WrappedElement<List<Object>>(handle, name);
|
||||||
|
this.elementType = container.getSubType();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(NbtVisitor visitor) {
|
public boolean accept(NbtVisitor visitor) {
|
||||||
@ -209,7 +219,7 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, Iterable<T
|
|||||||
protected NbtBase<TType> toOuter(Object inner) {
|
protected NbtBase<TType> toOuter(Object inner) {
|
||||||
if (inner == null)
|
if (inner == null)
|
||||||
return null;
|
return null;
|
||||||
return NbtFactory.fromNMS(inner);
|
return NbtFactory.fromNMS(inner, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -59,7 +59,7 @@ public class NbtBinarySerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return NbtFactory.fromNMS(methodLoad.invoke(null, source));
|
return NbtFactory.fromNMS(methodLoad.invoke(null, source), null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new FieldAccessException("Unable to read NBT from " + source, e);
|
throw new FieldAccessException("Unable to read NBT from " + source, e);
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.bukkit.plugin.PluginDescriptionFile;
|
import org.bukkit.plugin.PluginDescriptionFile;
|
||||||
@ -26,6 +25,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest;
|
|||||||
import com.comphenix.protocol.ProtocolLibrary;
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.io.Files;
|
||||||
|
|
||||||
// Damn final classes ...
|
// Damn final classes ...
|
||||||
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
|
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
|
||||||
@ -91,13 +91,14 @@ public class SimpleCraftBukkitITCase {
|
|||||||
* Copy ProtocolLib into the plugins folder.
|
* Copy ProtocolLib into the plugins folder.
|
||||||
* @throws IOException If anything went wrong.
|
* @throws IOException If anything went wrong.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
private static void setupPlugins() throws IOException {
|
private static void setupPlugins() throws IOException {
|
||||||
File pluginDirectory = new File("plugins/");
|
File pluginDirectory = new File("plugins/");
|
||||||
File bestFile = null;
|
File bestFile = null;
|
||||||
int bestLength = Integer.MAX_VALUE;
|
int bestLength = Integer.MAX_VALUE;
|
||||||
|
|
||||||
// Copy the ProtocolLib plugin to the server
|
// Copy the ProtocolLib plugin to the server
|
||||||
FileUtils.cleanDirectory(pluginDirectory);
|
Files.deleteDirectoryContents(pluginDirectory);
|
||||||
|
|
||||||
for (File file : new File("../").listFiles()) {
|
for (File file : new File("../").listFiles()) {
|
||||||
String name = file.getName();
|
String name = file.getName();
|
||||||
@ -107,7 +108,7 @@ public class SimpleCraftBukkitITCase {
|
|||||||
bestFile = file;
|
bestFile = file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FileUtils.copyFile(bestFile, new File(pluginDirectory, bestFile.getName()));
|
Files.copy(bestFile, new File(pluginDirectory, bestFile.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,10 +4,13 @@ import static org.mockito.Matchers.any;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import net.minecraft.server.v1_6_R3.StatisticList;
|
import net.minecraft.server.v1_7_R1.Block;
|
||||||
|
import net.minecraft.server.v1_7_R1.Item;
|
||||||
|
import net.minecraft.server.v1_7_R1.RegistryMaterials;
|
||||||
|
import net.minecraft.server.v1_7_R1.StatisticList;
|
||||||
|
|
||||||
// Will have to be updated for every version though
|
// Will have to be updated for every version though
|
||||||
import org.bukkit.craftbukkit.v1_6_R3.inventory.CraftItemFactory;
|
import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemFactory;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
@ -37,9 +40,12 @@ public class BukkitInitialization {
|
|||||||
initializePackage();
|
initializePackage();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
StatisticList.b();
|
Block.p();
|
||||||
|
Item.l();
|
||||||
|
StatisticList.a();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Swallow
|
// Swallow
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock the server object
|
// Mock the server object
|
||||||
@ -48,6 +54,7 @@ public class BukkitInitialization {
|
|||||||
ItemMeta mockedMeta = mock(ItemMeta.class);
|
ItemMeta mockedMeta = mock(ItemMeta.class);
|
||||||
|
|
||||||
when(mockedServer.getItemFactory()).thenReturn(mockedFactory);
|
when(mockedServer.getItemFactory()).thenReturn(mockedFactory);
|
||||||
|
when(mockedServer.isPrimaryThread()).thenReturn(true);
|
||||||
when(mockedFactory.getItemMeta(any(Material.class))).thenReturn(mockedMeta);
|
when(mockedFactory.getItemMeta(any(Material.class))).thenReturn(mockedMeta);
|
||||||
|
|
||||||
// Inject this fake server
|
// Inject this fake server
|
||||||
@ -63,6 +70,6 @@ public class BukkitInitialization {
|
|||||||
*/
|
*/
|
||||||
public static void initializePackage() {
|
public static void initializePackage() {
|
||||||
// Initialize reflection
|
// Initialize reflection
|
||||||
MinecraftReflection.setMinecraftPackage("net.minecraft.server.v1_6_R3", "org.bukkit.craftbukkit.v1_6_R3");
|
MinecraftReflection.setMinecraftPackage("net.minecraft.server.v1_7_R1", "org.bukkit.craftbukkit.v1_7_R1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,15 +22,15 @@ import java.lang.reflect.Array;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import net.minecraft.server.v1_6_R3.AttributeModifier;
|
import net.minecraft.server.v1_7_R1.AttributeModifier;
|
||||||
import net.minecraft.server.v1_6_R3.AttributeSnapshot;
|
import net.minecraft.server.v1_7_R1.AttributeSnapshot;
|
||||||
import net.minecraft.server.v1_6_R3.Packet44UpdateAttributes;
|
import net.minecraft.server.v1_7_R1.PacketPlayOutUpdateAttributes;
|
||||||
|
|
||||||
import org.apache.commons.lang.SerializationUtils;
|
import org.apache.commons.lang.SerializationUtils;
|
||||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang.builder.ToStringStyle;
|
import org.apache.commons.lang.builder.ToStringStyle;
|
||||||
// Will have to be updated for every version though
|
// Will have to be updated for every version though
|
||||||
import org.bukkit.craftbukkit.v1_6_R3.inventory.CraftItemFactory;
|
import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemFactory;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.WorldType;
|
import org.bukkit.WorldType;
|
||||||
@ -43,6 +43,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||||
|
|
||||||
import com.comphenix.protocol.BukkitInitialization;
|
import com.comphenix.protocol.BukkitInitialization;
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.Packets;
|
||||||
import com.comphenix.protocol.injector.PacketConstructor;
|
import com.comphenix.protocol.injector.PacketConstructor;
|
||||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||||
@ -120,7 +121,7 @@ public class PacketContainerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetShorts() {
|
public void testGetShorts() {
|
||||||
PacketContainer itemData = new PacketContainer(Packets.Server.ITEM_DATA);
|
PacketContainer itemData = new PacketContainer(Packets.Server.TRANSACTION);
|
||||||
testPrimitive(itemData.getShorts(), 0, (short)0, (short)1);
|
testPrimitive(itemData.getShorts(), 0, (short)0, (short)1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +151,7 @@ public class PacketContainerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetStrings() {
|
public void testGetStrings() {
|
||||||
PacketContainer explosion = new PacketContainer(Packets.Server.CHAT);
|
PacketContainer explosion = new PacketContainer(PacketType.Play.Client.CHAT);
|
||||||
testPrimitive(explosion.getStrings(), 0, null, "hello");
|
testPrimitive(explosion.getStrings(), 0, null, "hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,11 +184,12 @@ public class PacketContainerTest {
|
|||||||
StructureModifier<ItemStack> items = windowClick.getItemModifier();
|
StructureModifier<ItemStack> items = windowClick.getItemModifier();
|
||||||
ItemStack goldAxe = new ItemStack(Material.GOLD_AXE);
|
ItemStack goldAxe = new ItemStack(Material.GOLD_AXE);
|
||||||
|
|
||||||
|
assertNotNull(goldAxe.getType());
|
||||||
assertNull(items.read(0));
|
assertNull(items.read(0));
|
||||||
|
|
||||||
// Insert the goldaxe and check if it's there
|
// Insert the goldaxe and check if it's there
|
||||||
items.write(0, goldAxe);
|
items.write(0, goldAxe);
|
||||||
assertTrue(equivalentItem(goldAxe, items.read(0)));
|
assertTrue("Item " + goldAxe + " != " + items.read(0), equivalentItem(goldAxe, items.read(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -292,10 +294,12 @@ public class PacketContainerTest {
|
|||||||
List<ChunkPosition> positions = Lists.newArrayList();
|
List<ChunkPosition> positions = Lists.newArrayList();
|
||||||
positions.add(new ChunkPosition(1, 2, 3));
|
positions.add(new ChunkPosition(1, 2, 3));
|
||||||
positions.add(new ChunkPosition(3, 4, 5));
|
positions.add(new ChunkPosition(3, 4, 5));
|
||||||
|
|
||||||
// Insert and read back
|
// Insert and read back
|
||||||
positionAccessor.write(0, positions);
|
positionAccessor.write(0, positions);
|
||||||
assertEquals(positions, positionAccessor.read(0));
|
List<ChunkPosition> cloned = positionAccessor.read(0);
|
||||||
|
|
||||||
|
assertEquals(positions, cloned);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -319,7 +323,7 @@ public class PacketContainerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSerialization() {
|
public void testSerialization() {
|
||||||
PacketContainer chat = new PacketContainer(3);
|
PacketContainer chat = new PacketContainer(PacketType.Play.Client.CHAT);
|
||||||
chat.getStrings().write(0, "Test");
|
chat.getStrings().write(0, "Test");
|
||||||
|
|
||||||
PacketContainer copy = (PacketContainer) SerializationUtils.clone(chat);
|
PacketContainer copy = (PacketContainer) SerializationUtils.clone(chat);
|
||||||
@ -337,7 +341,7 @@ public class PacketContainerTest {
|
|||||||
List<AttributeModifier> modifiers = Lists.newArrayList(
|
List<AttributeModifier> modifiers = Lists.newArrayList(
|
||||||
new AttributeModifier(UUID.randomUUID(), "Unknown synced attribute modifier", 10, 0));
|
new AttributeModifier(UUID.randomUUID(), "Unknown synced attribute modifier", 10, 0));
|
||||||
AttributeSnapshot snapshot = new AttributeSnapshot(
|
AttributeSnapshot snapshot = new AttributeSnapshot(
|
||||||
(Packet44UpdateAttributes) attribute.getHandle(), "generic.Maxhealth", 20.0, modifiers);
|
(PacketPlayOutUpdateAttributes) attribute.getHandle(), "generic.Maxhealth", 20.0, modifiers);
|
||||||
|
|
||||||
attribute.getSpecificModifier(List.class).write(0, Lists.newArrayList(snapshot));
|
attribute.getSpecificModifier(List.class).write(0, Lists.newArrayList(snapshot));
|
||||||
PacketContainer cloned = attribute.deepClone();
|
PacketContainer cloned = attribute.deepClone();
|
||||||
@ -367,25 +371,17 @@ public class PacketContainerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testDeepClone() {
|
public void testDeepClone() {
|
||||||
// Try constructing all the packets
|
// Try constructing all the packets
|
||||||
for (Integer id : Iterables.concat(
|
for (PacketType type : PacketType.values()) {
|
||||||
Packets.getClientRegistry().values(),
|
|
||||||
Packets.getServerRegistry().values() )) {
|
|
||||||
|
|
||||||
// Whether or not this packet has been registered
|
// Whether or not this packet has been registered
|
||||||
boolean registered = Packets.Server.isSupported(id) ||
|
boolean registered = type.isSupported();
|
||||||
Packets.Client.isSupported(id);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PacketContainer constructed = new PacketContainer(id);
|
PacketContainer constructed = new PacketContainer(type);
|
||||||
|
|
||||||
if (!registered) {
|
if (!registered) {
|
||||||
fail("Expected IllegalArgumentException(Packet " + id + " not registered");
|
fail("Expected IllegalArgumentException(Packet " + type + " not registered");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure these packets contains fields as well
|
|
||||||
assertTrue("Constructed packet with no known fields (" + id + ")",
|
|
||||||
constructed.getModifier().size() > 0);
|
|
||||||
|
|
||||||
// Initialize default values
|
// Initialize default values
|
||||||
constructed.getModifier().writeDefaults();
|
constructed.getModifier().writeDefaults();
|
||||||
|
|
||||||
@ -396,18 +392,23 @@ public class PacketContainerTest {
|
|||||||
StructureModifier<Object> firstMod = constructed.getModifier(), secondMod = cloned.getModifier();
|
StructureModifier<Object> firstMod = constructed.getModifier(), secondMod = cloned.getModifier();
|
||||||
assertEquals(firstMod.size(), secondMod.size());
|
assertEquals(firstMod.size(), secondMod.size());
|
||||||
|
|
||||||
// Make sure all the fields are equivalent
|
if (PacketType.Status.Server.KICK_DISCONNECT.equals(type)) {
|
||||||
for (int i = 0; i < firstMod.size(); i++) {
|
assertArrayEquals(SerializationUtils.serialize(constructed), SerializationUtils.serialize(cloned));
|
||||||
if (firstMod.getField(i).getType().isArray())
|
|
||||||
assertArrayEquals(getArray(firstMod.read(i)), getArray(secondMod.read(i)));
|
} else {
|
||||||
else
|
// Make sure all the fields are equivalent
|
||||||
testEquality(firstMod.read(i), secondMod.read(i));
|
for (int i = 0; i < firstMod.size(); i++) {
|
||||||
|
if (firstMod.getField(i).getType().isArray())
|
||||||
|
assertArrayEquals(getArray(firstMod.read(i)), getArray(secondMod.read(i)));
|
||||||
|
else
|
||||||
|
testEquality(firstMod.read(i), secondMod.read(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
if (!registered) {
|
if (!registered) {
|
||||||
// Check the same
|
// Check the same
|
||||||
assertEquals(e.getMessage(), "The packet ID " + id + " is not registered.");
|
assertEquals(e.getMessage(), "The packet ID " + type + " is not registered.");
|
||||||
} else {
|
} else {
|
||||||
// Something is very wrong
|
// Something is very wrong
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -8,10 +8,10 @@ import java.io.DataInputStream;
|
|||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import net.minecraft.server.v1_6_R3.IntHashMap;
|
import net.minecraft.server.v1_7_R1.IntHashMap;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.craftbukkit.v1_6_R3.inventory.CraftItemFactory;
|
import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemFactory;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -4,9 +4,9 @@ import static org.junit.Assert.*;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import net.minecraft.server.v1_6_R3.AttributeModifier;
|
import net.minecraft.server.v1_7_R1.AttributeModifier;
|
||||||
import net.minecraft.server.v1_6_R3.AttributeSnapshot;
|
import net.minecraft.server.v1_7_R1.AttributeSnapshot;
|
||||||
import net.minecraft.server.v1_6_R3.Packet44UpdateAttributes;
|
import net.minecraft.server.v1_7_R1.PacketPlayOutUpdateAttributes;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
@ -83,7 +83,7 @@ public class WrappedAttributeTest {
|
|||||||
modifiers.add((AttributeModifier) wrapper.getHandle());
|
modifiers.add((AttributeModifier) wrapper.getHandle());
|
||||||
}
|
}
|
||||||
return new AttributeSnapshot(
|
return new AttributeSnapshot(
|
||||||
(Packet44UpdateAttributes) attribute.getParentPacket().getHandle(),
|
(PacketPlayOutUpdateAttributes) attribute.getParentPacket().getHandle(),
|
||||||
attribute.getAttributeKey(), attribute.getBaseValue(), modifiers);
|
attribute.getAttributeKey(), attribute.getBaseValue(), modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ import java.io.DataOutput;
|
|||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.craftbukkit.v1_6_R3.inventory.CraftItemFactory;
|
import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemFactory;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren