diff --git a/src/main/java/us/myles/ViaVersion/packets/PacketType.java b/src/main/java/us/myles/ViaVersion/packets/PacketType.java index 1291a508b..945e674db 100644 --- a/src/main/java/us/myles/ViaVersion/packets/PacketType.java +++ b/src/main/java/us/myles/ViaVersion/packets/PacketType.java @@ -60,6 +60,7 @@ public enum PacketType { PLAY_SPAWN_MOB(State.PLAY, Direction.OUTGOING, 0x0F, 0x03), PLAY_SPAWN_PAINTING(State.PLAY, Direction.OUTGOING, 0x10, 0x04), PLAY_SPAWN_PLAYER(State.PLAY, Direction.OUTGOING, 0x0C, 0x05), + PLAY_ANIMATION(State.PLAY, Direction.OUTGOING, 0x0B, 0x06), PLAY_STATS(State.PLAY, Direction.OUTGOING, 0x37, 0x07), PLAY_BLOCK_BREAK_ANIMATION(State.PLAY, Direction.OUTGOING, 0x25, 0x08), diff --git a/src/main/java/us/myles/ViaVersion2/api/PacketWrapper.java b/src/main/java/us/myles/ViaVersion2/api/PacketWrapper.java new file mode 100644 index 000000000..ab91ad7ad --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/PacketWrapper.java @@ -0,0 +1,57 @@ +package us.myles.ViaVersion2.api; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.data.UserConnection; +import us.myles.ViaVersion2.api.type.Type; +import us.myles.ViaVersion2.api.util.Pair; + +import java.util.ArrayList; +import java.util.List; + +public class PacketWrapper { + private final ByteBuf inputBuffer; + private final UserConnection userConnection; + private List> packetValues = new ArrayList<>(); + + public PacketWrapper(ByteBuf inputBuffer, UserConnection userConnection) { + this.inputBuffer = inputBuffer; + this.userConnection = userConnection; + } + + public T get(Type type, int index) { + int currentIndex = 0; + for (Pair packetValue : packetValues) { + if (packetValue.getKey() == type) { // Ref check + if (currentIndex == index) { + return (T) packetValue.getValue(); + } + currentIndex++; + } + } + throw new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index); + } + + public T read(Type type) { + // We could in the future log input read values, but honestly for things like bulk maps, mem waste D: + return type.read(inputBuffer); + } + + + public void write(Type type, T value) { + packetValues.add(new Pair(type, value)); + } + + public void writeToBuffer(ByteBuf buffer) { + for (Pair packetValue : packetValues) { + packetValue.getKey().write(buffer, packetValue.getValue()); + } + } + + public void writeRemaining(ByteBuf output) { + output.writeBytes(inputBuffer); + } + + public UserConnection user() { + return this.userConnection; + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/data/StoredObject.java b/src/main/java/us/myles/ViaVersion2/api/data/StoredObject.java new file mode 100644 index 000000000..5384bde9d --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/data/StoredObject.java @@ -0,0 +1,4 @@ +package us.myles.ViaVersion2.api.data; + +public class StoredObject { +} diff --git a/src/main/java/us/myles/ViaVersion2/api/data/UserConnection.java b/src/main/java/us/myles/ViaVersion2/api/data/UserConnection.java new file mode 100644 index 000000000..9ea0fd148 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/data/UserConnection.java @@ -0,0 +1,28 @@ +package us.myles.ViaVersion2.api.data; + +import java.util.ArrayList; +import java.util.List; + +public class UserConnection { + List storedObjects = new ArrayList<>(); + + public T object(Class objectClass) { + for (StoredObject o : storedObjects) { + if (o.getClass().equals(objectClass)) + return (T) o; + } + return null; + } + + public boolean has(Class objectClass) { + for (StoredObject o : storedObjects) { + if (o.getClass().equals(objectClass)) + return true; + } + return false; + } + + public void add(StoredObject object) { + storedObjects.add(object); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/protocol/Protocol.java b/src/main/java/us/myles/ViaVersion2/api/protocol/Protocol.java new file mode 100644 index 000000000..b9b9e6c09 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/protocol/Protocol.java @@ -0,0 +1,78 @@ +package us.myles.ViaVersion2.api.protocol; + +import io.netty.buffer.ByteBuf; +import lombok.AllArgsConstructor; +import lombok.Getter; +import us.myles.ViaVersion.packets.Direction; +import us.myles.ViaVersion.packets.State; +import us.myles.ViaVersion2.api.PacketWrapper; +import us.myles.ViaVersion2.api.util.Pair; +import us.myles.ViaVersion2.api.data.UserConnection; +import us.myles.ViaVersion2.api.remapper.PacketRemapper; +import us.myles.ViaVersion2.api.type.Type; + +import java.util.HashMap; +import java.util.Map; + +public abstract class Protocol { + public abstract void registerPackets(); + + public abstract void init(UserConnection userConnection); + + public Map, ProtocolPacket> incoming = new HashMap<>(); + public Map, ProtocolPacket> outgoing = new HashMap<>(); + + public void registerIncoming(State state, int oldPacketID, int newPacketID) { + registerIncoming(state, oldPacketID, newPacketID, null); + } + + public void registerIncoming(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) { + ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper); + incoming.put(new Pair<>(state, newPacketID), protocolPacket); + } + + public void registerOutgoing(State state, int oldPacketID, int newPacketID) { + registerOutgoing(state, oldPacketID, newPacketID, null); + } + + public void registerOutgoing(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) { + ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper); + outgoing.put(new Pair<>(state, oldPacketID), protocolPacket); + } + + public void transform(Direction direction, State state, int packetID, PacketWrapper packetWrapper, ByteBuf output) { + Pair statePacket = new Pair<>(state, packetID); + Map, ProtocolPacket> packetMap = (direction == Direction.OUTGOING ? outgoing : incoming); + ProtocolPacket protocolPacket; + if (packetMap.containsKey(statePacket)) { + protocolPacket = packetMap.get(statePacket); + } else { + System.out.println("Packet not found: " + packetID); + // simply translate + Type.VAR_INT.write(output, packetID); + // pass through + packetWrapper.writeRemaining(output); + return; + } + // write packet id + Type.VAR_INT.write(output, direction == Direction.OUTGOING ? protocolPacket.getNewID() : protocolPacket.getOldID()); + // remap + if (protocolPacket.getRemapper() != null) { + protocolPacket.getRemapper().remap(packetWrapper); + // write to output + packetWrapper.writeToBuffer(output); + } else { + // pass through + packetWrapper.writeRemaining(output); + } + } + + @AllArgsConstructor + @Getter + class ProtocolPacket { + State state; + int oldID; + int newID; + PacketRemapper remapper; + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/protocol1_9to1_8/Protocol1_9TO1_8.java b/src/main/java/us/myles/ViaVersion2/api/protocol1_9to1_8/Protocol1_9TO1_8.java new file mode 100644 index 000000000..0c9885b47 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/protocol1_9to1_8/Protocol1_9TO1_8.java @@ -0,0 +1,20 @@ +package us.myles.ViaVersion2.api.protocol1_9to1_8; + +import us.myles.ViaVersion2.api.data.UserConnection; +import us.myles.ViaVersion2.api.protocol.Protocol; +import us.myles.ViaVersion2.api.protocol1_9to1_8.packets.SpawnPackets; +import us.myles.ViaVersion2.api.protocol1_9to1_8.storage.EntityTracker; + +public class Protocol1_9TO1_8 extends Protocol { + @Override + public void registerPackets() { + // Example PLAY_SPAWN_OBJECT(State.PLAY, Direction.OUTGOING, 0x0E, 0x00), + SpawnPackets.register(this); + } + + @Override + public void init(UserConnection userConnection) { + // Entity tracker + userConnection.add(new EntityTracker()); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/protocol1_9to1_8/packets/SpawnPackets.java b/src/main/java/us/myles/ViaVersion2/api/protocol1_9to1_8/packets/SpawnPackets.java new file mode 100644 index 000000000..aa45d9b47 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/protocol1_9to1_8/packets/SpawnPackets.java @@ -0,0 +1,161 @@ +package us.myles.ViaVersion2.api.protocol1_9to1_8.packets; + +import us.myles.ViaVersion.packets.State; +import us.myles.ViaVersion2.api.PacketWrapper; +import us.myles.ViaVersion2.api.protocol.Protocol; +import us.myles.ViaVersion2.api.protocol1_9to1_8.storage.EntityTracker; +import us.myles.ViaVersion2.api.remapper.PacketRemapper; +import us.myles.ViaVersion2.api.remapper.ValueCreator; +import us.myles.ViaVersion2.api.type.Type; + +public class SpawnPackets { + public static void register(Protocol protocol) { + // Spawn Object Packet + protocol.registerOutgoing(State.PLAY, 0x0E, 0x00, new PacketRemapper() { + @Override + public void registerMap() { + map(Type.VAR_INT); // 0 - Entity ID + // 1 - UUID + create(new ValueCreator() { + @Override + public void write(PacketWrapper wrapper) { + int entityID = wrapper.get(Type.VAR_INT, 0); + EntityTracker tracker = wrapper.user().object(EntityTracker.class); + wrapper.write(Type.UUID, tracker.getEntityUUID(entityID)); + } + }); + map(Type.BYTE); // 2 - Type + + map(Type.INT, Type.DOUBLE); // 3 - X - Needs to be divide by 32 + map(Type.INT, Type.DOUBLE); // 4 - Y - Needs to be divide by 32 + map(Type.INT, Type.DOUBLE); // 5 - Z - Needs to be divide by 32 + + map(Type.BYTE); // 6 - Pitch + map(Type.BYTE); // 7 - Yaw + + map(Type.INT); // 8 - Data + + // Create last 3 shorts + create(new ValueCreator() { + @Override + public void write(PacketWrapper wrapper) { + int data = wrapper.get(Type.INT, 3); // Data (4th Integer) + + short vX = 0, vY = 0, vZ = 0; + if (data > 0) { + vX = wrapper.read(Type.SHORT); + vY = wrapper.read(Type.SHORT); + vZ = wrapper.read(Type.SHORT); + } + + wrapper.write(Type.SHORT, vX); + wrapper.write(Type.SHORT, vY); + wrapper.write(Type.SHORT, vZ); + } + }); + + } + }); + + // Spawn XP Packet + protocol.registerOutgoing(State.PLAY, 0x11, 0x01, new PacketRemapper() { + @Override + public void registerMap() { + map(Type.VAR_INT); // 0 - Entity ID + + map(Type.INT, Type.DOUBLE); // 1 - X - Needs to be divide by 32 + map(Type.INT, Type.DOUBLE); // 2 - Y - Needs to be divide by 32 + map(Type.INT, Type.DOUBLE); // 3 - Z - Needs to be divide by 32 + + map(Type.INT); // 4 - Data + } + }); + + // Spawn Global Entity Packet + protocol.registerOutgoing(State.PLAY, 0x2C, 0x02, new PacketRemapper() { + @Override + public void registerMap() { + map(Type.VAR_INT); // 0 - Entity ID + map(Type.BYTE); // 1 - Type + + map(Type.INT, Type.DOUBLE); // 2 - X - Needs to be divide by 32 + map(Type.INT, Type.DOUBLE); // 3 - Y - Needs to be divide by 32 + map(Type.INT, Type.DOUBLE); // 4 - Z - Needs to be divide by 32 + } + }); + + // Spawn Mob Packet + protocol.registerOutgoing(State.PLAY, 0x0F, 0x03, new PacketRemapper() { + @Override + public void registerMap() { + map(Type.VAR_INT); // 0 - Entity ID + // 1 - UUID + create(new ValueCreator() { + @Override + public void write(PacketWrapper wrapper) { + int entityID = wrapper.get(Type.VAR_INT, 0); + EntityTracker tracker = wrapper.user().object(EntityTracker.class); + wrapper.write(Type.UUID, tracker.getEntityUUID(entityID)); + } + }); + map(Type.UNSIGNED_BYTE); // 2 - Type + + map(Type.INT, Type.DOUBLE); // 3 - X - Needs to be divide by 32 + map(Type.INT, Type.DOUBLE); // 4 - Y - Needs to be divide by 32 + map(Type.INT, Type.DOUBLE); // 5 - Z - Needs to be divide by 32 + + map(Type.BYTE); // 6 - Yaw + map(Type.BYTE); // 7 - Pitch + map(Type.BYTE); // 8 - Head Pitch + + map(Type.SHORT); // 9 - Velocity X + map(Type.SHORT); // 10 - Velocity Y + map(Type.SHORT); // 11 - Velocity Z + + // TODO Read Metadata + } + }); + + // Spawn Painting Packet + protocol.registerOutgoing(State.PLAY, 0x10, 0x04, new PacketRemapper() { + @Override + public void registerMap() { + map(Type.VAR_INT); // 0 - Entity ID + // 1 - UUID + create(new ValueCreator() { + @Override + public void write(PacketWrapper wrapper) { + int entityID = wrapper.get(Type.VAR_INT, 0); + EntityTracker tracker = wrapper.user().object(EntityTracker.class); + wrapper.write(Type.UUID, tracker.getEntityUUID(entityID)); + } + }); + map(Type.STRING); // 2 - Title + + map(Type.POSITION); // 3 - Position + + map(Type.BYTE); // 4 - Direction + } + }); + + // Spawn Player Packet + protocol.registerOutgoing(State.PLAY, 0x0C, 0x05, new PacketRemapper() { + @Override + public void registerMap() { + map(Type.VAR_INT); // 0 - Entity ID + map(Type.UUID); // 1 - Player UUID + + map(Type.INT, Type.DOUBLE); // 2 - X - Needs to be divide by 32 + map(Type.INT, Type.DOUBLE); // 3 - Y - Needs to be divide by 32 + map(Type.INT, Type.DOUBLE); // 4 - Z - Needs to be divide by 32 + + map(Type.BYTE); // 5 - Yaw + map(Type.BYTE); // 6 - Pitch + + map(Type.SHORT, Type.NOTHING); // Current Item is discontinued + + // TODO Read Metadata + } + }); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/protocol1_9to1_8/storage/EntityTracker.java b/src/main/java/us/myles/ViaVersion2/api/protocol1_9to1_8/storage/EntityTracker.java new file mode 100644 index 000000000..e12093083 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/protocol1_9to1_8/storage/EntityTracker.java @@ -0,0 +1,26 @@ +package us.myles.ViaVersion2.api.protocol1_9to1_8.storage; + +import lombok.Getter; +import org.bukkit.entity.EntityType; +import us.myles.ViaVersion2.api.data.StoredObject; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Getter +public class EntityTracker extends StoredObject{ + private final Map uuidMap = new HashMap<>(); + private final Map clientEntityTypes = new HashMap<>(); + private final Map vehicleMap = new HashMap<>(); + + public UUID getEntityUUID(int id) { + if (uuidMap.containsKey(id)) { + return uuidMap.get(id); + } else { + UUID uuid = UUID.randomUUID(); + uuidMap.put(id, uuid); + return uuid; + } + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/remapper/PacketRemapper.java b/src/main/java/us/myles/ViaVersion2/api/remapper/PacketRemapper.java new file mode 100644 index 000000000..dcd89d681 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/remapper/PacketRemapper.java @@ -0,0 +1,46 @@ +package us.myles.ViaVersion2.api.remapper; + +import us.myles.ViaVersion2.api.PacketWrapper; +import us.myles.ViaVersion2.api.util.Pair; +import us.myles.ViaVersion2.api.type.Type; + +import java.util.ArrayList; +import java.util.List; + +public abstract class PacketRemapper { + private List> valueRemappers = new ArrayList<>(); + + public PacketRemapper() { + registerMap(); + } + + public void map(Type type) { + TypeRemapper remapper = new TypeRemapper(type); + map(remapper, remapper); + } + + public void map(Type oldType, Type newType) { + map(new TypeRemapper(oldType), new TypeRemapper(newType)); + } + + public void map(ValueReader inputRemapper, ValueTransformer outputRemapper) { + valueRemappers.add(new Pair(inputRemapper, outputRemapper)); + } + + public void create(ValueCreator transformer) { + map(new TypeRemapper(Type.NOTHING), transformer); + } + + public abstract void registerMap(); + + public void remap(PacketWrapper packetWrapper) { + // Read all the current values + for(Pair valueRemapper : valueRemappers){ + Object object = valueRemapper.getKey().read(packetWrapper); + // Convert object to write type :O!!! + // TODO: Data converter lol + valueRemapper.getValue().write(packetWrapper, object); + } + // If we had handlers we'd put them here + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/remapper/TypeRemapper.java b/src/main/java/us/myles/ViaVersion2/api/remapper/TypeRemapper.java new file mode 100644 index 000000000..7b15f2a53 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/remapper/TypeRemapper.java @@ -0,0 +1,22 @@ +package us.myles.ViaVersion2.api.remapper; + +import us.myles.ViaVersion2.api.PacketWrapper; +import us.myles.ViaVersion2.api.type.Type; + +public class TypeRemapper implements ValueReader, ValueTransformer { + private final Type type; + + public TypeRemapper(Type type) { + this.type = type; + } + + @Override + public T read(PacketWrapper wrapper) { + return wrapper.read(type); + } + + @Override + public void write(PacketWrapper output, T inputValue) { + output.write(type, inputValue); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/remapper/ValueCreator.java b/src/main/java/us/myles/ViaVersion2/api/remapper/ValueCreator.java new file mode 100644 index 000000000..cef6dae39 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/remapper/ValueCreator.java @@ -0,0 +1,12 @@ +package us.myles.ViaVersion2.api.remapper; + +import us.myles.ViaVersion2.api.PacketWrapper; + +public abstract class ValueCreator implements ValueTransformer{ + public abstract void write(PacketWrapper wrapper); + + @Override + public void write(PacketWrapper writer, Object inputValue) { + write(writer); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/remapper/ValueReader.java b/src/main/java/us/myles/ViaVersion2/api/remapper/ValueReader.java new file mode 100644 index 000000000..81326ea72 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/remapper/ValueReader.java @@ -0,0 +1,8 @@ +package us.myles.ViaVersion2.api.remapper; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.PacketWrapper; + +public interface ValueReader { + public T read(PacketWrapper wrapper); +} diff --git a/src/main/java/us/myles/ViaVersion2/api/remapper/ValueTransformer.java b/src/main/java/us/myles/ViaVersion2/api/remapper/ValueTransformer.java new file mode 100644 index 000000000..18331a5e5 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/remapper/ValueTransformer.java @@ -0,0 +1,7 @@ +package us.myles.ViaVersion2.api.remapper; + +import us.myles.ViaVersion2.api.PacketWrapper; + +public interface ValueTransformer { + public void write(PacketWrapper writer, T inputValue); +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/ByteBufReader.java b/src/main/java/us/myles/ViaVersion2/api/type/ByteBufReader.java new file mode 100644 index 000000000..88359c6a9 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/ByteBufReader.java @@ -0,0 +1,7 @@ +package us.myles.ViaVersion2.api.type; + +import io.netty.buffer.ByteBuf; + +public interface ByteBufReader { + public T read(ByteBuf buffer); +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/ByteBufWriter.java b/src/main/java/us/myles/ViaVersion2/api/type/ByteBufWriter.java new file mode 100644 index 000000000..570fabb5c --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/ByteBufWriter.java @@ -0,0 +1,7 @@ +package us.myles.ViaVersion2.api.type; + +import io.netty.buffer.ByteBuf; + +public interface ByteBufWriter { + public void write(ByteBuf buffer, T object); +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/Type.java b/src/main/java/us/myles/ViaVersion2/api/type/Type.java new file mode 100644 index 000000000..562cd4cac --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/Type.java @@ -0,0 +1,63 @@ +package us.myles.ViaVersion2.api.type; + + +import javafx.geometry.Pos; +import lombok.Getter; +import us.myles.ViaVersion2.api.type.types.*; +import us.myles.ViaVersion2.api.util.Position; + +import java.util.UUID; + +@Getter +public abstract class Type implements ByteBufReader, ByteBufWriter { + /* Defined Types */ + public static final Type BYTE = new ByteType(); + public static final Type BYTE_ARRAY = new ArrayType<>(Type.BYTE); + + public static final Type UNSIGNED_BYTE = new UnsignedByteType(); + public static final Type UNSIGNED_BYTE_ARRAY = new ArrayType<>(Type.UNSIGNED_BYTE); + + public static final Type BOOLEAN = new BooleanType(); + public static final Type BOOLEAN_ARRAY = new ArrayType<>(Type.BOOLEAN); + /* Number Types */ + public static final Type INT = new IntType(); + public static final Type INT_ARRAY = new ArrayType<>(Type.INT); + + public static final Type DOUBLE = new DoubleType(); + public static final Type DOUBLE_ARRAY = new ArrayType<>(Type.DOUBLE); + + public static final Type LONG = new LongType(); + public static final Type LONG_ARRAY = new ArrayType<>(Type.LONG); + + public static final Type FLOAT = new FloatType(); + public static final Type FLOAT_ARRAY = new ArrayType<>(Type.FLOAT); + + public static final Type SHORT = new ShortType(); + public static final Type SHORT_ARRAY = new ArrayType<>(Type.SHORT); + /* Other Types */ + public static final Type STRING = new StringType(); + public static final Type STRING_ARRAY = new ArrayType<>(Type.STRING); + + public static final Type UUID = new UUIDType(); + public static final Type UUID_ARRAY = new ArrayType<>(Type.UUID); + /* Variable Types */ + public static final Type VAR_INT = new VarIntType(); + public static final Type VAR_INT_ARRAY = new ArrayType<>(Type.VAR_INT); + /* Special Types */ + public static final Type NOTHING = new VoidType(); // This is purely used for remapping. + /* MC Types */ + public static final Type POSITION = new PositionType(); // This is purely used for remapping. + /* Actual Class */ + + private final Class outputClass; + private final String typeName; + + public Type(Class outputClass) { + this(outputClass.getSimpleName(), outputClass); + } + + public Type(String typeName, Class outputClass) { + this.outputClass = outputClass; + this.typeName = typeName; + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/ArrayType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/ArrayType.java new file mode 100644 index 000000000..169dba013 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/ArrayType.java @@ -0,0 +1,65 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class ArrayType extends Type { + private final Type elementType; + + public ArrayType(Type type) { + super(type.getTypeName() + " Array", (Class) getArrayClass(type.getOutputClass())); + this.elementType = type; + } + + @Override + public T[] read(ByteBuf buffer) { + int amount = Type.VAR_INT.read(buffer); + Object[] array = new Object[amount]; + for (int i = 0; i < amount; i++) { + array[i] = elementType.read(buffer); + } + return (T[]) array; + } + + @Override + public void write(ByteBuf buffer, T[] object) { + Type.VAR_INT.write(buffer, object.length); + for (T o : object) { + elementType.write(buffer, o); + } + } + + /* Taken from http://stackoverflow.com/questions/4901128/obtaining-the-array-class-of-a-component-type */ + public static Class getArrayClass(Class componentType) { + ClassLoader classLoader = componentType.getClassLoader(); + String name; + if (componentType.isArray()) { + // just add a leading "[" + name = "[" + componentType.getName(); + } else if (componentType == boolean.class) { + name = "[Z"; + } else if (componentType == byte.class) { + name = "[B"; + } else if (componentType == char.class) { + name = "[C"; + } else if (componentType == double.class) { + name = "[D"; + } else if (componentType == float.class) { + name = "[F"; + } else if (componentType == int.class) { + name = "[I"; + } else if (componentType == long.class) { + name = "[J"; + } else if (componentType == short.class) { + name = "[S"; + } else { + // must be an object non-array class + name = "[L" + componentType.getName() + ";"; + } + try { + return classLoader != null ? classLoader.loadClass(name) : Class.forName(name); + } catch (ClassNotFoundException e) { + return null; // oh + } + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/BooleanType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/BooleanType.java new file mode 100644 index 000000000..db5d98071 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/BooleanType.java @@ -0,0 +1,20 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class BooleanType extends Type { + public BooleanType() { + super(Boolean.class); + } + + @Override + public Boolean read(ByteBuf buffer) { + return buffer.readBoolean(); + } + + @Override + public void write(ByteBuf buffer, Boolean object) { + buffer.writeBoolean(object); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/ByteType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/ByteType.java new file mode 100644 index 000000000..62b63cf09 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/ByteType.java @@ -0,0 +1,20 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class ByteType extends Type { + public ByteType() { + super(Byte.class); + } + + @Override + public Byte read(ByteBuf buffer) { + return buffer.readByte(); + } + + @Override + public void write(ByteBuf buffer, Byte object) { + buffer.writeByte(object); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/DoubleType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/DoubleType.java new file mode 100644 index 000000000..688099d5a --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/DoubleType.java @@ -0,0 +1,20 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class DoubleType extends Type { + public DoubleType() { + super(Double.class); + } + + @Override + public Double read(ByteBuf buffer) { + return buffer.readDouble(); + } + + @Override + public void write(ByteBuf buffer, Double object) { + buffer.writeDouble(object); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/FloatType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/FloatType.java new file mode 100644 index 000000000..90051445d --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/FloatType.java @@ -0,0 +1,20 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class FloatType extends Type { + public FloatType() { + super(Float.class); + } + + @Override + public Float read(ByteBuf buffer) { + return buffer.readFloat(); + } + + @Override + public void write(ByteBuf buffer, Float object) { + buffer.writeFloat(object); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/IntType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/IntType.java new file mode 100644 index 000000000..4ff966c4e --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/IntType.java @@ -0,0 +1,20 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class IntType extends Type { + public IntType() { + super(Integer.class); + } + + @Override + public Integer read(ByteBuf buffer) { + return buffer.readInt(); + } + + @Override + public void write(ByteBuf buffer, Integer object) { + buffer.writeInt(object); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/LongType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/LongType.java new file mode 100644 index 000000000..5475dcb16 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/LongType.java @@ -0,0 +1,20 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class LongType extends Type { + public LongType() { + super(Long.class); + } + + @Override + public Long read(ByteBuf buffer) { + return buffer.readLong(); + } + + @Override + public void write(ByteBuf buffer, Long object) { + buffer.writeLong(object); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/PositionType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/PositionType.java new file mode 100644 index 000000000..e58c00a73 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/PositionType.java @@ -0,0 +1,27 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; +import us.myles.ViaVersion2.api.util.Position; + +public class PositionType extends Type { + public PositionType() { + super(Position.class); + } + + @Override + public Position read(ByteBuf buffer) { + long val = buffer.readLong(); + long x = (val >> 38); // signed + long y = (val >> 26) & 0xfff; // unsigned + // this shifting madness is used to preserve sign + long z = (val << 38) >> 38; // signed + + return new Position(x, y, z); + } + + @Override + public void write(ByteBuf buffer, Position object) { + buffer.writeLong(((object.getX() & 0x3ffffff) << 38) | ((object.getY() & 0xfff) << 26) | (object.getZ() & 0x3ffffff)); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/ShortType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/ShortType.java new file mode 100644 index 000000000..d48f19994 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/ShortType.java @@ -0,0 +1,20 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class ShortType extends Type { + public ShortType() { + super(Short.class); + } + + @Override + public Short read(ByteBuf buffer) { + return buffer.readShort(); + } + + @Override + public void write(ByteBuf buffer, Short object) { + buffer.writeShort(object); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/StringType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/StringType.java new file mode 100644 index 000000000..7476feb43 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/StringType.java @@ -0,0 +1,32 @@ +package us.myles.ViaVersion2.api.type.types; + +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class StringType extends Type { + public StringType() { + super(String.class); + } + + @Override + public String read(ByteBuf buffer) { + int len = Type.VAR_INT.read(buffer); + Preconditions.checkArgument(len <= Short.MAX_VALUE, "Cannot receive string longer than Short.MAX_VALUE (got %s characters)", len); + + byte[] b = new byte[len]; + buffer.readBytes(b); + + return new String(b, Charsets.UTF_8); + } + + @Override + public void write(ByteBuf buffer, String object) { + Preconditions.checkArgument(object.length() <= Short.MAX_VALUE, "Cannot send string longer than Short.MAX_VALUE (got %s characters)", object.length()); + + byte[] b = object.getBytes(Charsets.UTF_8); + Type.VAR_INT.write(buffer, b.length); + buffer.writeBytes(b); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/UUIDType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/UUIDType.java new file mode 100644 index 000000000..7cb6c1a66 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/UUIDType.java @@ -0,0 +1,23 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +import java.util.UUID; + +public class UUIDType extends Type { + public UUIDType() { + super(UUID.class); + } + + @Override + public UUID read(ByteBuf buffer) { + return new UUID(buffer.readLong(), buffer.readLong()); + } + + @Override + public void write(ByteBuf buffer, UUID object) { + buffer.writeLong(object.getMostSignificantBits()); + buffer.writeLong(object.getLeastSignificantBits()); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/UnsignedByteType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/UnsignedByteType.java new file mode 100644 index 000000000..4217536be --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/UnsignedByteType.java @@ -0,0 +1,20 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class UnsignedByteType extends Type { + public UnsignedByteType() { + super(Short.class); + } + + @Override + public Short read(ByteBuf buffer) { + return buffer.readUnsignedByte(); + } + + @Override + public void write(ByteBuf buffer, Short object) { + buffer.writeByte(object); + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/VarIntType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/VarIntType.java new file mode 100644 index 000000000..8aa2b3609 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/VarIntType.java @@ -0,0 +1,52 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class VarIntType extends Type{ + + public VarIntType() { + super("VarInt", Integer.class); + } + + @Override + public void write(ByteBuf buffer, Integer object) { + int part; + while (true) { + part = object & 0x7F; + + object >>>= 7; + if (object != 0) { + part |= 0x80; + } + + buffer.writeByte(part); + + if (object == 0) { + break; + } + } + } + + @Override + public Integer read(ByteBuf buffer) { + int out = 0; + int bytes = 0; + byte in; + while (true) { + in = buffer.readByte(); + + out |= (in & 0x7F) << (bytes++ * 7); + + if (bytes > 5) { // 5 is maxBytes + throw new RuntimeException("VarInt too big"); + } + + if ((in & 0x80) != 0x80) { + break; + } + } + + return out; + } +} \ No newline at end of file diff --git a/src/main/java/us/myles/ViaVersion2/api/type/types/VoidType.java b/src/main/java/us/myles/ViaVersion2/api/type/types/VoidType.java new file mode 100644 index 000000000..3d753653f --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/type/types/VoidType.java @@ -0,0 +1,20 @@ +package us.myles.ViaVersion2.api.type.types; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion2.api.type.Type; + +public class VoidType extends Type { + public VoidType() { + super(Void.class); + } + + @Override + public Void read(ByteBuf buffer) { + return null; + } + + @Override + public void write(ByteBuf buffer, Void object) { + + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/util/Pair.java b/src/main/java/us/myles/ViaVersion2/api/util/Pair.java new file mode 100644 index 000000000..ed6b35e0b --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/util/Pair.java @@ -0,0 +1,16 @@ +package us.myles.ViaVersion2.api.util; + +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode +public class Pair { + private final X key; + private final Y value; + + public Pair(X key, Y value){ + this.key = key; + this.value = value; + } +} diff --git a/src/main/java/us/myles/ViaVersion2/api/util/Position.java b/src/main/java/us/myles/ViaVersion2/api/util/Position.java new file mode 100644 index 000000000..3ad5e0002 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion2/api/util/Position.java @@ -0,0 +1,12 @@ +package us.myles.ViaVersion2.api.util; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class Position { + private Long x; + private Long y; + private Long z; +}