Add protocol registry.

Fix some issues with PacketWrapper
Add 1.9.1 port
Add ProtocolVersion detection on boot
Add more type converters
Implement Pipeline properly
This commit is contained in:
Myles 2016-03-17 21:24:25 +00:00
parent 7a98cdd28f
commit 56d5f8eec6
20 changed files with 370 additions and 28 deletions

View File

@ -28,6 +28,7 @@ import us.myles.ViaVersion.util.Configuration;
import us.myles.ViaVersion.util.ListWrapper;
import us.myles.ViaVersion.util.ReflectionUtil;
import us.myles.ViaVersion2.api.data.UserConnection;
import us.myles.ViaVersion2.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion2.api.protocol.base.ProtocolInfo;
import java.io.File;
@ -75,7 +76,22 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
return;
}
getLogger().info("ViaVersion " + getDescription().getVersion() + " is now enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
// Gather version :)
Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
@Override
public void run() {
gatherProtocolVersion();
// Check if there are any pipes to this version
if (ProtocolRegistry.SERVER_PROTOCOL != -1) {
getLogger().info("ViaVersion detected protocol version: " + ProtocolRegistry.SERVER_PROTOCOL);
if (!ProtocolRegistry.isWorkingPipe()) {
getLogger().warning("ViaVersion will not function on the current protocol.");
}
}
}
});
getLogger().info("ViaVersion " + getDescription().getVersion() + " is now enabled, injecting.");
injectPacketHandler();
if (getConfig().getBoolean("simulate-pt", true))
new ViaIdleThread(portedPlayers).runTaskTimerAsynchronously(this, 1L, 1L); // Updates player's idle status
@ -97,6 +113,53 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
getCommand("viaversion").setExecutor(new ViaVersionCommand(this));
}
public void gatherProtocolVersion() {
try {
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
Class<?> pingClazz = ReflectionUtil.nms("ServerPing");
Object ping = null;
// Search for ping method
for (Field f : serverClazz.getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType().getSimpleName().equals("ServerPing")) {
f.setAccessible(true);
ping = f.get(server);
}
}
}
if (ping != null) {
Object serverData = null;
for (Field f : pingClazz.getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType().getSimpleName().endsWith("ServerData")) {
f.setAccessible(true);
serverData = f.get(ping);
}
}
}
if (serverData != null) {
int protocolVersion = -1;
for (Field f : serverData.getClass().getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType() == int.class) {
f.setAccessible(true);
protocolVersion = (int) f.get(serverData);
}
}
}
if (protocolVersion != -1) {
ProtocolRegistry.SERVER_PROTOCOL = protocolVersion;
return;
}
}
}
} catch (Exception e) {
e.printStackTrace();
// We couldn't work it out... We'll just use ping and hope for the best...
}
}
public void generateConfig() {
File file = new File(getDataFolder(), "config.yml");
if (file.exists()) {

View File

@ -60,6 +60,7 @@ public class PacketWrapper {
}
public <T> T read(Type<T> type) throws Exception {
if(type == Type.NOTHING) return null;
if (readableObjects.isEmpty()) {
Preconditions.checkNotNull(inputBuffer, "This packet does not have an input buffer.");
// We could in the future log input read values, but honestly for things like bulk maps, mem waste D:
@ -69,7 +70,11 @@ public class PacketWrapper {
if (read.getKey().equals(type)) {
return (T) read.getValue();
} else {
throw new IOException("Unable to read type " + type.getTypeName() + ", found " + type.getTypeName());
if (type == Type.NOTHING) {
return read(type); // retry
} else {
throw new IOException("Unable to read type " + type.getTypeName() + ", found " + read.getKey().getTypeName());
}
}
}
}
@ -89,9 +94,13 @@ public class PacketWrapper {
if (id != -1) {
Type.VAR_INT.write(buffer, id);
}
if (readableObjects.size() > 0) {
packetValues.addAll(readableObjects);
}
int index = 0;
for (Pair<Type, Object> packetValue : packetValues) {
try {
try {
Object value = packetValue.getValue();
if (value != null) {
if (!packetValue.getKey().getOutputClass().isAssignableFrom(value.getClass())) {
@ -116,6 +125,7 @@ public class PacketWrapper {
public void clearInputBuffer() {
if (inputBuffer != null)
inputBuffer.clear();
readableObjects.clear(); // :(
}
private void writeRemaining(ByteBuf output) {
@ -154,5 +164,6 @@ public class PacketWrapper {
public void resetReader() {
this.readableObjects.addAll(packetValues);
this.packetValues.clear();
}
}

View File

@ -7,7 +7,6 @@ import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion2.api.PacketWrapper;
import us.myles.ViaVersion2.api.data.UserConnection;
import us.myles.ViaVersion2.api.protocol1_9to1_8.Protocol1_9TO1_8;
import us.myles.ViaVersion2.api.remapper.PacketRemapper;
import us.myles.ViaVersion2.api.util.Pair;
@ -59,11 +58,16 @@ public abstract class Protocol {
// remap
if (protocolPacket.getRemapper() != null) {
protocolPacket.getRemapper().remap(packetWrapper);
if(packetWrapper.isCancelled())
if (packetWrapper.isCancelled())
throw new CancelException();
}
}
@Override
public String toString() {
return "Protocol:" + getClass().getSimpleName();
}
@AllArgsConstructor
@Getter
class ProtocolPacket {

View File

@ -8,7 +8,9 @@ import us.myles.ViaVersion2.api.protocol.base.BaseProtocol;
import us.myles.ViaVersion2.api.protocol.base.ProtocolInfo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class ProtocolPipeline extends Protocol {
LinkedList<Protocol> protocolList;
@ -54,7 +56,13 @@ public class ProtocolPipeline extends Protocol {
@Override
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
// System.out.println("--> Packet ID incoming: " + packetWrapper.getId() + " - " + state);
for (Protocol protocol : new ArrayList<>(protocolList)) { // Copy to prevent from removal.
List<Protocol> protocols = new ArrayList<>(protocolList);
// Other way if outgoing
if (direction == Direction.OUTGOING)
Collections.reverse(protocols);
for (Protocol protocol : protocols) { // Copy to prevent from removal.
System.out.println("Calling " + protocol.getClass().getSimpleName() + " " + direction);
protocol.transform(direction, state, packetWrapper);
// Reset the reader for the packetWrapper (So it can be recycled across packets)
packetWrapper.resetReader();

View File

@ -0,0 +1,76 @@
package us.myles.ViaVersion2.api.protocol;
import us.myles.ViaVersion2.api.protocol1_9_1to1_9.Protocol1_9_1TO1_9;
import us.myles.ViaVersion2.api.protocol1_9to1_8.Protocol1_9TO1_8;
import us.myles.ViaVersion2.api.util.Pair;
import java.util.*;
public class ProtocolRegistry {
// Input Version -> Output Version & Protocol (Allows fast lookup)
private static Map<Integer, Map<Integer, Protocol>> registryMap = new HashMap<>();
public static int SERVER_PROTOCOL = -1;
static {
// Register built in protocols
registerProtocol(new Protocol1_9TO1_8(), Arrays.asList(ProtocolVersion.V1_9), ProtocolVersion.V1_8);
registerProtocol(new Protocol1_9_1TO1_9(), Arrays.asList(ProtocolVersion.V1_9_1_PRE2), ProtocolVersion.V1_9);
}
public static void registerProtocol(Protocol protocol, List<Integer> supported, Integer output) {
for (Integer version : supported) {
if (!registryMap.containsKey(version)) {
registryMap.put(version, new HashMap<Integer, Protocol>());
}
registryMap.get(version).put(output, protocol);
}
}
public static boolean isWorkingPipe() {
for (Map<Integer, Protocol> maps : registryMap.values()) {
if (maps.containsKey(SERVER_PROTOCOL)) return true;
}
return false; // No destination for protocol
}
private static List<Pair<Integer, Protocol>> getProtocolPath(List<Pair<Integer, Protocol>> current, int clientVersion, int serverVersion) {
if (current.size() > 50) return null; // Fail safe, protocol too complicated.
// First check if there is any protocols for this
if (!registryMap.containsKey(clientVersion)) {
return null; // Not supported
}
// Next check there isn't an obvious path
Map<Integer, Protocol> inputMap = registryMap.get(clientVersion);
if (inputMap.containsKey(serverVersion)) {
current.add(new Pair<>(serverVersion, inputMap.get(serverVersion)));
return current; // Easy solution
}
// There might be a more advanced solution... So we'll see if any of the others can get us there
for (Map.Entry<Integer, Protocol> entry : inputMap.entrySet()) {
// Ensure it wasn't caught by the other loop
if (!entry.getKey().equals(serverVersion)) {
Pair<Integer, Protocol> pair = new Pair<>(entry.getKey(), entry.getValue());
// Ensure no recursion
if (!current.contains(pair)) {
// Create a copy
List<Pair<Integer, Protocol>> newCurrent = new ArrayList<>(current);
newCurrent.add(pair);
// Calculate the rest of the protocol using the current
newCurrent = getProtocolPath(newCurrent, entry.getKey(), serverVersion);
if (newCurrent != null) {
return newCurrent;
}
}
}
}
return null;
}
public static List<Pair<Integer, Protocol>> getProtocolPath(int clientVersion, int serverVersion) {
return getProtocolPath(new ArrayList<Pair<Integer, Protocol>>(), clientVersion, serverVersion);
}
}

View File

@ -0,0 +1,12 @@
package us.myles.ViaVersion2.api.protocol;
public class ProtocolVersion {
/* Defined protocol constants */
public static int V1_7_1 = 4;
public static int V1_7_6 = 5;
public static int V1_8 = 47;
public static int V1_9 = 107;
public static int V1_9_1_PRE2 = 108;
}

View File

@ -6,15 +6,17 @@ import org.json.simple.parser.ParseException;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion2.api.PacketWrapper;
import us.myles.ViaVersion2.api.data.UserConnection;
import us.myles.ViaVersion2.api.protocol.Protocol;
import us.myles.ViaVersion2.api.protocol1_9to1_8.Protocol1_9TO1_8;
import us.myles.ViaVersion2.api.protocol.ProtocolPipeline;
import us.myles.ViaVersion2.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion2.api.remapper.PacketHandler;
import us.myles.ViaVersion2.api.remapper.PacketRemapper;
import us.myles.ViaVersion2.api.type.Type;
import us.myles.ViaVersion2.api.util.Pair;
import java.util.List;
import java.util.UUID;
public class BaseProtocol extends Protocol {
@ -29,14 +31,25 @@ public class BaseProtocol extends Protocol {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) {
// TODO: Actually make this show compatible versions
ProtocolInfo info = wrapper.user().get(ProtocolInfo.class);
String originalStatus = wrapper.get(Type.STRING, 0);
try {
JSONObject json = (JSONObject) new JSONParser().parse(originalStatus);
JSONObject version = (JSONObject) json.get("version");
version.put("protocol", info.getProtocolVersion());
System.out.println("Calculating " + info.getProtocolVersion());
// TODO: Make it so the detection doesn't base off just ping :)
if (ProtocolRegistry.SERVER_PROTOCOL == -1) {
Long original = (Long) version.get("protocol");
ProtocolRegistry.SERVER_PROTOCOL = original.intValue();
}
List<Pair<Integer, Protocol>> protocols = ProtocolRegistry.getProtocolPath(info.getProtocolVersion(), ProtocolRegistry.SERVER_PROTOCOL);
if (protocols != null) {
version.put("protocol", info.getProtocolVersion());
} else {
// not compatible :(, *plays very sad violin*
wrapper.user().setActive(false);
}
wrapper.set(Type.STRING, 0, json.toJSONString()); // Update value
} catch (ParseException e) {
e.printStackTrace();
@ -88,15 +101,27 @@ public class BaseProtocol extends Protocol {
@Override
public void handle(PacketWrapper wrapper) {
int protVer = wrapper.get(Type.VAR_INT, 0);
int state = wrapper.get(Type.VAR_INT, 1);
ProtocolInfo info = wrapper.user().get(ProtocolInfo.class);
info.setProtocolVersion(protVer);
// TODO: Choose the right pipe
// We'll just cheat lol
wrapper.user().get(ProtocolInfo.class).getPipeline().add(new Protocol1_9TO1_8());
System.out.println("I should decide on a protocol for " + protVer);
wrapper.set(Type.VAR_INT, 0, 47); // TODO remove hard code
// Choose the pipe
List<Pair<Integer, Protocol>> protocols = ProtocolRegistry.getProtocolPath(info.getProtocolVersion(), ProtocolRegistry.SERVER_PROTOCOL);
ProtocolPipeline pipeline = wrapper.user().get(ProtocolInfo.class).getPipeline();
if (protocols != null) {
for (Pair<Integer, Protocol> prot : protocols) {
pipeline.add(prot.getValue());
System.out.println("adding pipe");
}
wrapper.set(Type.VAR_INT, 0, ProtocolRegistry.SERVER_PROTOCOL);
} else {
if (state == 2) {
// not compatible :(, *plays very sad violin*
wrapper.user().setActive(false);
}
}
// Change state
int state = wrapper.get(Type.VAR_INT, 1);
if (state == 1) {
info.setState(State.STATUS);
}

View File

@ -0,0 +1,33 @@
package us.myles.ViaVersion2.api.protocol1_9_1to1_9;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion2.api.data.UserConnection;
import us.myles.ViaVersion2.api.protocol.Protocol;
import us.myles.ViaVersion2.api.remapper.PacketRemapper;
import us.myles.ViaVersion2.api.type.Type;
public class Protocol1_9_1TO1_9 extends Protocol{
@Override
protected void registerPackets() {
// Currently supports 1.9.1 PRE 2
// Join Game Packet
registerOutgoing(State.PLAY, 0x23, 0x23, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.INT); // 0 - Player ID
map(Type.UNSIGNED_BYTE); // 1 - Player Gamemode
// 1.9.1 PRE 2 Changed this
map(Type.BYTE, Type.INT); // 2 - Player Dimension
map(Type.UNSIGNED_BYTE); // 3 - World Difficulty
map(Type.UNSIGNED_BYTE); // 4 - Max Players (Tab)
map(Type.STRING); // 5 - Level Type
map(Type.BOOLEAN); // 6 - Reduced Debug info
}
});
}
@Override
public void init(UserConnection userConnection) {
}
}

View File

@ -56,7 +56,6 @@ public class Protocol1_9TO1_8 extends Protocol {
@Override
protected void registerPackets() {
System.out.println("Registering packets for 1.9 to 1.8");
SpawnPackets.register(this);
InventoryPackets.register(this);
EntityPackets.register(this);

View File

@ -2,8 +2,9 @@ package us.myles.ViaVersion2.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion2.api.type.Type;
import us.myles.ViaVersion2.api.type.TypeConverter;
public class BooleanType extends Type<Boolean> {
public class BooleanType extends Type<Boolean> implements TypeConverter<Boolean>{
public BooleanType() {
super(Boolean.class);
}
@ -17,4 +18,13 @@ public class BooleanType extends Type<Boolean> {
public void write(ByteBuf buffer, Boolean object) {
buffer.writeBoolean(object);
}
@Override
public Boolean from(Object o) {
if (o instanceof Number) {
return ((Number) o).intValue() == 1;
}
return (Boolean) o;
}
}

View File

@ -19,14 +19,14 @@ public class ByteType extends Type<Byte> implements TypeConverter<Byte> {
buffer.writeByte(object);
}
@Override
public Byte from(Object o) {
if (o instanceof Number) {
return ((Number) o).byteValue();
}
if(o instanceof Boolean){
if(o == true) return 1;
if(o == false) return 0;
return ((Boolean)o) == true ? (byte) 1 : 0;
}
return (Byte) o;
}

View File

@ -2,8 +2,9 @@ package us.myles.ViaVersion2.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion2.api.type.Type;
import us.myles.ViaVersion2.api.type.TypeConverter;
public class DoubleType extends Type<Double> {
public class DoubleType extends Type<Double> implements TypeConverter<Double>{
public DoubleType() {
super(Double.class);
}
@ -17,4 +18,15 @@ public class DoubleType extends Type<Double> {
public void write(ByteBuf buffer, Double object) {
buffer.writeDouble(object);
}
@Override
public Double from(Object o) {
if (o instanceof Number) {
return ((Number) o).doubleValue();
}
if(o instanceof Boolean){
return ((Boolean)o) == true ? 1D : 0D;
}
return (Double) o;
}
}

View File

@ -2,8 +2,9 @@ package us.myles.ViaVersion2.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion2.api.type.Type;
import us.myles.ViaVersion2.api.type.TypeConverter;
public class FloatType extends Type<Float> {
public class FloatType extends Type<Float> implements TypeConverter<Float>{
public FloatType() {
super(Float.class);
}
@ -17,4 +18,16 @@ public class FloatType extends Type<Float> {
public void write(ByteBuf buffer, Float object) {
buffer.writeFloat(object);
}
@Override
public Float from(Object o) {
if (o instanceof Number) {
return ((Number) o).floatValue();
}
if(o instanceof Boolean){
return ((Boolean)o) == true ? 1F : 0;
}
return (Float) o;
}
}

View File

@ -2,8 +2,9 @@ package us.myles.ViaVersion2.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion2.api.type.Type;
import us.myles.ViaVersion2.api.type.TypeConverter;
public class IntType extends Type<Integer> {
public class IntType extends Type<Integer> implements TypeConverter<Integer>{
public IntType() {
super(Integer.class);
}
@ -17,4 +18,15 @@ public class IntType extends Type<Integer> {
public void write(ByteBuf buffer, Integer object) {
buffer.writeInt(object);
}
@Override
public Integer from(Object o) {
if (o instanceof Number) {
return ((Number) o).intValue();
}
if(o instanceof Boolean){
return ((Boolean)o) == true ? 1 : 0;
}
return (Integer) o;
}
}

View File

@ -2,8 +2,9 @@ package us.myles.ViaVersion2.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion2.api.type.Type;
import us.myles.ViaVersion2.api.type.TypeConverter;
public class LongType extends Type<Long> {
public class LongType extends Type<Long> implements TypeConverter<Long> {
public LongType() {
super(Long.class);
}
@ -17,4 +18,16 @@ public class LongType extends Type<Long> {
public void write(ByteBuf buffer, Long object) {
buffer.writeLong(object);
}
@Override
public Long from(Object o) {
if (o instanceof Number) {
return ((Number) o).longValue();
}
if(o instanceof Boolean){
return ((Boolean)o) == true ? 1L : 0;
}
return (Long) o;
}
}

View File

@ -2,8 +2,9 @@ package us.myles.ViaVersion2.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion2.api.type.Type;
import us.myles.ViaVersion2.api.type.TypeConverter;
public class ShortType extends Type<Short> {
public class ShortType extends Type<Short> implements TypeConverter<Short> {
public ShortType() {
super(Short.class);
}
@ -17,4 +18,15 @@ public class ShortType extends Type<Short> {
public void write(ByteBuf buffer, Short object) {
buffer.writeShort(object);
}
@Override
public Short from(Object o) {
if (o instanceof Number) {
return ((Number) o).shortValue();
}
if(o instanceof Boolean){
return ((Boolean)o) == true ? (short) 1 : 0;
}
return (short) o;
}
}

View File

@ -2,8 +2,9 @@ package us.myles.ViaVersion2.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion2.api.type.Type;
import us.myles.ViaVersion2.api.type.TypeConverter;
public class UnsignedByteType extends Type<Short> {
public class UnsignedByteType extends Type<Short> implements TypeConverter<Short> {
public UnsignedByteType() {
super(Short.class);
}
@ -17,4 +18,15 @@ public class UnsignedByteType extends Type<Short> {
public void write(ByteBuf buffer, Short object) {
buffer.writeByte(object);
}
@Override
public Short from(Object o) {
if (o instanceof Number) {
return ((Number) o).shortValue();
}
if(o instanceof Boolean){
return ((Boolean)o) == true ? (short) 1 : 0;
}
return (short) o;
}
}

View File

@ -2,8 +2,9 @@ package us.myles.ViaVersion2.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion2.api.type.Type;
import us.myles.ViaVersion2.api.type.TypeConverter;
public class UnsignedShortType extends Type<Integer> {
public class UnsignedShortType extends Type<Integer> implements TypeConverter<Integer> {
public UnsignedShortType() {
super(Integer.class);
}
@ -17,4 +18,15 @@ public class UnsignedShortType extends Type<Integer> {
public void write(ByteBuf buffer, Integer object) {
buffer.writeShort(object);
}
@Override
public Integer from(Object o) {
if (o instanceof Number) {
return ((Number) o).intValue();
}
if(o instanceof Boolean){
return ((Boolean)o) == true ? 1 : 0;
}
return (Integer) o;
}
}

View File

@ -2,8 +2,9 @@ package us.myles.ViaVersion2.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion2.api.type.Type;
import us.myles.ViaVersion2.api.type.TypeConverter;
public class VarIntType extends Type<Integer>{
public class VarIntType extends Type<Integer> implements TypeConverter<Integer> {
public VarIntType() {
super("VarInt", Integer.class);
@ -49,4 +50,16 @@ public class VarIntType extends Type<Integer>{
return out;
}
@Override
public Integer from(Object o) {
if (o instanceof Number) {
return ((Number) o).intValue();
}
if (o instanceof Boolean) {
return ((Boolean) o) == true ? 1 : 0;
}
return (Integer) o;
}
}

View File

@ -3,10 +3,12 @@ package us.myles.ViaVersion2.api.util;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@EqualsAndHashCode
@ToString
public class Pair<X, Y> {
private X key;
private Y value;