Merge pull request #2 from MylesIsCool/master

Update From Original #2
This commit is contained in:
HugoDaBosss 2016-03-02 11:44:24 +01:00
commit b6c2a46a86
19 changed files with 388 additions and 178 deletions

View File

@ -1,4 +1,4 @@
# ViaVersion 0.3.3
# ViaVersion 0.3.7
**Allows the connection of 1.8 clients to 1.9**
This plugin modifies netty to allow connection of 1.9 clients to 1.8,
@ -13,9 +13,6 @@ Remap spawn eggs
If you have a bug with entities, please report the full stack trace
Some items with JSON data cause crashing due to the change in how strict minecraft is.
This took hours of work, so if you enjoy this consider looking into contacting me and supporting my projects.
@ -32,10 +29,12 @@ Contributors:
**Myself** (harhar)
**Matsv/StatBoom**
**Matsv/StamBoom**
**HugoDaBosss**
**SanderGielisse**
License:
--------

View File

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.data.game.chunk;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.PacketUtil;
import us.myles.ViaVersion.util.PacketUtil;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -39,7 +39,7 @@ public class NetUtil {
public static Column readOldChunkData(int x, int z, boolean isFullChunk, int bitmask, byte[] input, boolean checkForSky, boolean hasSkyLight) {
int pos = 0;
int expected = 0;
int expected = isFullChunk ? 256 : 0;
boolean sky = false;
ShortBuffer buf = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
// 0 = Calculate expected length and determine if the packet has skylight.
@ -85,7 +85,7 @@ public class NetUtil {
}
if(pass == 3) {
if(chunks[ind].getSkyLight() != null) {
if(sky) {
NibbleArray3d skylight = chunks[ind].getSkyLight();
System.arraycopy(input, pos, skylight.getData(), 0, skylight.getData().length);
pos += skylight.getData().length;

View File

@ -1,5 +1,6 @@
package us.myles.ViaVersion;
import io.netty.channel.socket.SocketChannel;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import us.myles.ViaVersion.packets.State;
@ -7,12 +8,17 @@ import us.myles.ViaVersion.packets.State;
import java.util.UUID;
public class ConnectionInfo {
private final SocketChannel channel;
private int protocol = 0;
private State state = State.HANDSHAKE;
private int compression = 0;
private Object lastPacket;
private java.util.UUID UUID;
public ConnectionInfo(SocketChannel socketChannel) {
this.channel = socketChannel;
}
public int getProtocol() {
return protocol;
}
@ -56,4 +62,8 @@ public class ConnectionInfo {
public Player getPlayer() {
return UUID == null ? null : Bukkit.getPlayer(UUID);
}
public SocketChannel getChannel() {
return channel;
}
}

View File

@ -8,18 +8,32 @@ import io.netty.channel.socket.SocketChannel;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.ViaVersionAPI;
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class Core extends JavaPlugin {
public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
private final Set<UUID> portedPlayers = Collections.newSetFromMap(new ConcurrentHashMap<UUID, Boolean>());
@Override
public void onEnable() {
ViaVersion.setInstance(this);
System.out.println("ViaVersion enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
try {
injectPacketHandler();
@ -30,6 +44,12 @@ public class Core extends JavaPlugin {
System.out.println("Unable to inject handlers, are you on 1.8? ");
e.printStackTrace();
}
Bukkit.getPluginManager().registerEvents(new Listener() {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent e) {
setPorted(e.getPlayer().getUniqueId(), false);
}
}, this);
}
public void injectPacketHandler() throws Exception {
@ -51,32 +71,22 @@ public class Core extends JavaPlugin {
}
}
@Override
public boolean isPorted(Player player) {
return portedPlayers.contains(player.getUniqueId());
}
public static Entity getEntity(final UUID player, final int id) {
try {
return Bukkit.getScheduler().callSyncMethod(getPlugin(Core.class), new Callable<Entity>() {
@Override
public Entity call() throws Exception {
Player p = Bukkit.getPlayer(player);
if (p == null) return null;
for (Entity e : p.getWorld().getEntities()) {
if (e.getEntityId() == id) {
return e;
}
}
return null;
}
}).get(10, TimeUnit.SECONDS);
} catch (Exception e) {
System.out.println("Error fetching entity ");
e.printStackTrace();
return null;
public void setPorted(UUID id, boolean value) {
if (value) {
portedPlayers.add(id);
} else {
portedPlayers.remove(id);
}
}
public static ItemStack getHandItem(final ConnectionInfo info) {
try {
return Bukkit.getScheduler().callSyncMethod(getPlugin(Core.class), new Callable<ItemStack>() {
return Bukkit.getScheduler().callSyncMethod(getPlugin(ViaVersionPlugin.class), new Callable<ItemStack>() {
@Override
public ItemStack call() throws Exception {
if (info.getPlayer() != null) {

View File

@ -0,0 +1,17 @@
package us.myles.ViaVersion.api;
public class ViaVersion {
private static ViaVersionAPI INSTANCE;
public static void setInstance(ViaVersionAPI api) {
if (INSTANCE != null) {
throw new IllegalStateException("Instance already set.");
}
INSTANCE = api;
}
public static ViaVersionAPI getInstance() {
return INSTANCE;
}
}

View File

@ -0,0 +1,8 @@
package us.myles.ViaVersion.api;
import org.bukkit.entity.Player;
public interface ViaVersionAPI {
boolean isPorted(Player player);
}

View File

@ -7,17 +7,15 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import us.myles.ViaVersion.CancelException;
import us.myles.ViaVersion.ConnectionInfo;
import us.myles.ViaVersion.PacketUtil;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.transformers.IncomingTransformer;
@ChannelHandler.Sharable
public class ViaInboundHandler extends ChannelInboundHandlerAdapter {
private final IncomingTransformer incomingTransformer;
private final ViaVersionInitializer init;
public ViaInboundHandler(Channel c, ConnectionInfo info, ViaVersionInitializer init) {
this.init = init;
this.incomingTransformer = new IncomingTransformer(c, info, init);
public ViaInboundHandler(ConnectionInfo info) {
this.incomingTransformer = new IncomingTransformer(info);
}
@Override

View File

@ -4,17 +4,17 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import us.myles.ViaVersion.CancelException;
import us.myles.ViaVersion.ConnectionInfo;
import us.myles.ViaVersion.PacketUtil;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.transformers.OutgoingTransformer;
@ChannelHandler.Sharable
public class ViaOutboundHandler extends ChannelOutboundHandlerAdapter {
private final OutgoingTransformer outgoingTransformer;
private final ViaVersionInitializer init;
private final ConnectionInfo info;
public ViaOutboundHandler(Channel c, ConnectionInfo info, ViaVersionInitializer init) {
this.init = init;
this.outgoingTransformer = new OutgoingTransformer(c, info, init);
public ViaOutboundHandler(ConnectionInfo info) {
this.info = info;
this.outgoingTransformer = new OutgoingTransformer(info);
}
@Override

View File

@ -3,7 +3,7 @@ package us.myles.ViaVersion.handlers;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import us.myles.ViaVersion.ConnectionInfo;
import us.myles.ViaVersion.ReflectionUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.Constructor;
@ -11,7 +11,7 @@ import java.lang.reflect.Constructor;
public class ViaOutboundPacketHandler extends ChannelOutboundHandlerAdapter {
private final ConnectionInfo info;
public ViaOutboundPacketHandler(Channel c, ConnectionInfo info) {
public ViaOutboundPacketHandler(ConnectionInfo info) {
this.info = info;
}

View File

@ -10,11 +10,6 @@ import java.lang.reflect.Method;
public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
private final ChannelInitializer<SocketChannel> oldInit;
private Method method;
private ConnectionInfo info;
private ViaInboundHandler inbound;
private ViaOutboundHandler outbound;
private SocketChannel socketChannel;
private ViaOutboundPacketHandler outbound2;
public ViaVersionInitializer(ChannelInitializer<SocketChannel> oldInit) {
this.oldInit = oldInit;
@ -28,24 +23,16 @@ public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
info = new ConnectionInfo();
ConnectionInfo info = new ConnectionInfo(socketChannel);
// Add originals
this.method.invoke(this.oldInit, socketChannel);
// Add our transformers
this.socketChannel = socketChannel;
this.inbound = new ViaInboundHandler(socketChannel, info, this);
this.outbound = new ViaOutboundHandler(socketChannel, info, this);
this.outbound2 = new ViaOutboundPacketHandler(socketChannel, info);
socketChannel.pipeline().addBefore("decoder", "via_incoming", this.inbound);
socketChannel.pipeline().addBefore("packet_handler", "via_outgoing2", this.outbound2);
socketChannel.pipeline().addBefore("encoder", "via_outgoing", this.outbound);
ViaInboundHandler inbound = new ViaInboundHandler(info);
ViaOutboundHandler outbound = new ViaOutboundHandler(info);
ViaOutboundPacketHandler outbound2 = new ViaOutboundPacketHandler(info);
socketChannel.pipeline().addBefore("decoder", "via_incoming", inbound);
socketChannel.pipeline().addBefore("packet_handler", "via_outgoing2", outbound2);
socketChannel.pipeline().addBefore("encoder", "via_outgoing", outbound);
}
public void remove(){
socketChannel.pipeline().remove("via_incoming");
socketChannel.pipeline().remove("via_outgoing");
socketChannel.pipeline().remove("via_outgoing2");
}
}

View File

@ -72,7 +72,7 @@ public enum MetaIndex {
// creeper
CREEPER_FUSE(Creeper.class, 16, Type.Byte, 11, NewType.VarInt), // -1 idle, 1 is fuse
CREEPER_ISPOWERED(Creeper.class, 17, Type.Byte, 12, NewType.Boolean),
CREEPER_ISIGNITED(Creeper.class, 18, Type.Byte, 13, NewType.Boolean), // TODO: Write on wiki.vg for current prot
CREEPER_ISIGNITED(Creeper.class, 18, Type.Byte, 13, NewType.Boolean),
// ghast
GHAST_ISATTACKING(Ghast.class, 16, Type.Byte, 11, NewType.Boolean),
// slime
@ -89,7 +89,7 @@ public enum MetaIndex {
WITHER_TARGET3(Wither.class, 19, Type.Int, 13, NewType.VarInt),
WITHER_INVULN_TIME(Wither.class, 20, Type.Int, 14, NewType.VarInt),
// guardian
GUARDIAN_INFO(Guardian.class, 16, Type.Byte, 11, NewType.Byte),
GUARDIAN_INFO(Guardian.class, 16, Type.Int, 11, NewType.Byte),
GUARDIAN_TARGET(Guardian.class, 17, Type.Int, 12, NewType.VarInt),
// boat
BOAT_SINCEHIT(Boat.class, 17, Type.Int, 5, NewType.VarInt),
@ -159,16 +159,6 @@ public enum MetaIndex {
return this.clazz;
}
public static MetaIndex getIndex(Entity entity, int index) {
EntityType type;
if (entity instanceof Player) {
type = EntityType.PLAYER;
} else {
type = entity.getType();
}
return getIndex(type, index);
}
public static MetaIndex getIndex(EntityType type, int index) {
Class<? extends org.bukkit.entity.Entity> entityClass = type.getEntityClass();
if(entityClass == null){

View File

@ -20,7 +20,7 @@ public enum PacketType {
/* Play serverbound */
PLAY_TP_CONFIRM(State.PLAY, Direction.INCOMING, -1, 0x00),
PLAY_TAB_COMPLETE_REQUEST(State.PLAY, Direction.INCOMING, 0x14, 0x01),
PLAY_CLICHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02),
PLAY_CHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02),
PLAY_CLIENT_STATUS(State.PLAY, Direction.INCOMING, 0x16, 0x03),
PLAY_CLIENT_SETTINGS(State.PLAY, Direction.INCOMING, 0x15, 0x04),
PLAY_CONFIRM_TRANS(State.PLAY, Direction.INCOMING, 0x0F, 0x05),

View File

@ -1,28 +1,23 @@
package us.myles.ViaVersion.transformers;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import us.myles.ViaVersion.*;
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
import us.myles.ViaVersion.CancelException;
import us.myles.ViaVersion.ConnectionInfo;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.packets.PacketType;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IncomingTransformer {
private final Channel channel;
private final ConnectionInfo info;
private final ViaVersionInitializer init;
public IncomingTransformer(Channel channel, ConnectionInfo info, ViaVersionInitializer init) {
this.channel = channel;
public IncomingTransformer(ConnectionInfo info) {
this.info = info;
this.init = init;
}
public void transform(int packetID, ByteBuf input, ByteBuf output) throws CancelException {
@ -49,8 +44,10 @@ public class IncomingTransformer {
PacketUtil.writeVarInt(protVer <= 102 ? protVer : 47, output); // pretend to be older
if (protVer <= 102) {
// Not 1.9 remove pipes
this.init.remove();
// not 1.9, remove pipes
info.getChannel().pipeline().remove("via_incoming");
info.getChannel().pipeline().remove("via_outgoing");
info.getChannel().pipeline().remove("via_outgoing2");
}
String serverAddress = PacketUtil.readString(input);
PacketUtil.writeString(serverAddress, output);
@ -108,7 +105,7 @@ public class IncomingTransformer {
try {
Class<?> setSlot = ReflectionUtil.nms("PacketPlayOutSetSlot");
Object setSlotPacket = setSlot.getConstructors()[1].newInstance(windowID, slot, null);
channel.writeAndFlush(setSlotPacket); // slot is empty
info.getChannel().writeAndFlush(setSlotPacket); // slot is empty
slot = -999; // we're evil, they'll throw item on the ground
} catch (ClassNotFoundException e) {
e.printStackTrace();
@ -178,7 +175,7 @@ public class IncomingTransformer {
output.writeByte(face);
int hand = PacketUtil.readVarInt(input);
ItemStack inHand = Core.getHandItem(info);
ItemStack inHand = ViaVersionPlugin.getHandItem(info);
Object item = null;
try {
Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class);
@ -205,12 +202,12 @@ public class IncomingTransformer {
}
if (packet == PacketType.PLAY_USE_ITEM) {
output.clear();
PacketUtil.writeVarInt(PacketType.PLAY_PLAYER_BLOCK_PLACEMENT.getPacketID(), output);
// Simulate using item :)
output.writeLong(-1L);
output.writeByte(255);
// write item in hand
ItemStack inHand = Core.getHandItem(info);
PacketUtil.writeVarInt(PacketType.PLAY_PLAYER_BLOCK_PLACEMENT.getPacketID(), output);
// Simulate using item :)
output.writeLong(-1L);
output.writeByte(255);
// write item in hand
ItemStack inHand = ViaVersionPlugin.getHandItem(info);
Object item = null;
try {
Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class);
@ -225,10 +222,10 @@ public class IncomingTransformer {
e.printStackTrace();
}
PacketUtil.writeItem(item, output);
output.writeByte(-1);
output.writeByte(-1);
output.writeByte(-1);
output.writeByte(-1);
output.writeByte(-1);
output.writeByte(-1);
return;
}
output.writeBytes(input);

View File

@ -2,43 +2,40 @@ package us.myles.ViaVersion.transformers;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.spacehq.mc.protocol.data.game.chunk.Column;
import org.spacehq.mc.protocol.util.NetUtil;
import us.myles.ViaVersion.*;
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
import us.myles.ViaVersion.CancelException;
import us.myles.ViaVersion.ConnectionInfo;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.metadata.MetaIndex;
import us.myles.ViaVersion.metadata.NewType;
import us.myles.ViaVersion.sounds.SoundEffect;
import us.myles.ViaVersion.metadata.Type;
import us.myles.ViaVersion.packets.PacketType;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.sounds.SoundEffect;
import us.myles.ViaVersion.util.EntityUtil;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import static us.myles.ViaVersion.PacketUtil.*;
import static us.myles.ViaVersion.util.PacketUtil.*;
public class OutgoingTransformer {
private static Gson gson = new Gson();
private final Channel channel;
private final ConnectionInfo info;
private final ViaVersionInitializer init;
private final ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
private boolean cancel = false;
private Map<Integer, UUID> uuidMap = new HashMap<Integer, UUID>();
private Map<Integer, EntityType> clientEntityTypes = new HashMap<Integer, EntityType>();
public OutgoingTransformer(Channel channel, ConnectionInfo info, ViaVersionInitializer init) {
this.channel = channel;
public OutgoingTransformer(ConnectionInfo info) {
this.info = info;
this.init = init;
}
public void transform(int packetID, ByteBuf input, ByteBuf output) throws CancelException {
@ -60,14 +57,13 @@ public class OutgoingTransformer {
// By default no transform
PacketUtil.writeVarInt(packetID, output);
if (packet == PacketType.PLAY_NAMED_SOUND_EFFECT) {
String name = PacketUtil.readString(input);
String name = PacketUtil.readString(input);
SoundEffect effect = SoundEffect.getByName(name);
int catid = 0;
String newname = name;
if(effect != null)
{
catid = effect.getCategory().getId();
newname = effect.getNewName();
if (effect != null) {
catid = effect.getCategory().getId();
newname = effect.getNewName();
}
PacketUtil.writeString(newname, output);
PacketUtil.writeVarInt(catid, output);
@ -77,22 +73,37 @@ public class OutgoingTransformer {
int passenger = input.readInt();
int vehicle = input.readInt();
boolean lead = input.readBoolean();
if (!lead){
if (!lead) {
output.clear();
writeVarInt(PacketType.PLAY_SET_PASSENGERS.getNewPacketID(),output);
writeVarInt(vehicle,output);
writeVarIntArray(Collections.singletonList(passenger),output);
writeVarInt(PacketType.PLAY_SET_PASSENGERS.getNewPacketID(), output);
writeVarInt(vehicle, output);
writeVarIntArray(Collections.singletonList(passenger), output);
return;
}
output.writeInt(passenger);
output.writeInt(vehicle);
return;
}
if (packet == PacketType.PLAY_DISCONNECT){
if (packet == PacketType.PLAY_DISCONNECT) {
String reason = readString(input);
if (reason.startsWith("\""))
reason = "{\"text\":" + reason + "}";
writeString(reason,output);
writeString(fixJson(reason), output);
return;
}
if (packet == PacketType.PLAY_TITLE) {
int action = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(action, output);
if (action == 0 || action == 1) {
String text = PacketUtil.readString(input);
PacketUtil.writeString(fixJson(text), output);
}
output.writeBytes(input);
return;
}
if (packet == PacketType.PLAY_PLAYER_LIST_HEADER_FOOTER) {
String header = readString(input);
String footer = readString(input);
writeString(fixJson(header), output);
writeString(fixJson(footer), output);
return;
}
if (packet == PacketType.PLAY_ENTITY_TELEPORT) {
@ -173,7 +184,9 @@ public class OutgoingTransformer {
if (packet == PacketType.LOGIN_SUCCESS) {
String uu = PacketUtil.readString(input);
PacketUtil.writeString(uu, output);
info.setUUID(UUID.fromString(uu));
UUID uniqueId = UUID.fromString(uu);
info.setUUID(uniqueId);
plugin.setPorted(uniqueId, true);
output.writeBytes(input);
return;
}
@ -201,13 +214,7 @@ public class OutgoingTransformer {
try {
List dw = ReflectionUtil.get(info.getLastPacket(), "b", List.class);
// get entity via entityID, not preferred but we need it.
Entity entity = Core.getEntity(info.getUUID(), id);
if (entity != null) {
transformMetadata(entity, dw, output);
} else {
// Died before we could get to it. rip
throw new CancelException();
}
transformMetadata(id, dw, output);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
@ -215,13 +222,42 @@ public class OutgoingTransformer {
}
return;
}
if (packet == PacketType.PLAY_SPAWN_OBJECT) {
if (packet == PacketType.PLAY_SPAWN_GLOBAL_ENTITY) {
int id = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(id, output);
// only used for lightning
byte type = input.readByte();
clientEntityTypes.put(id, EntityType.LIGHTNING);
output.writeByte(type);
double x = input.readInt();
output.writeDouble(x / 32D);
double y = input.readInt();
output.writeDouble(y / 32D);
double z = input.readInt();
output.writeDouble(z / 32D);
return;
}
if (packet == PacketType.PLAY_DESTROY_ENTITIES) {
int count = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(count, output);
int[] toDestroy = PacketUtil.readVarInts(count, input);
for (int entityID : toDestroy) {
clientEntityTypes.remove(entityID);
PacketUtil.writeVarInt(entityID, output);
}
return;
}
if (packet == PacketType.PLAY_SPAWN_OBJECT) {
int id = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(id, output);
PacketUtil.writeUUID(getUUID(id), output);
byte type = input.readByte();
clientEntityTypes.put(id, EntityUtil.getTypeFromID(type, true));
output.writeByte(type);
double x = input.readInt();
@ -237,13 +273,12 @@ public class OutgoingTransformer {
int data = input.readInt();
output.writeInt(data);
short vX = 0, vY = 0, vZ = 0;
if(data > 0)
{
vX = input.readShort();
vY = input.readShort();
vZ = input.readShort();
if (data > 0) {
vX = input.readShort();
vY = input.readShort();
vZ = input.readShort();
}
output.writeShort(vX);
output.writeShort(vY);
@ -251,8 +286,9 @@ public class OutgoingTransformer {
return;
}
if (packet == PacketType.PLAY_SPAWN_XP_ORB) { // TODO: Verify
if (packet == PacketType.PLAY_SPAWN_XP_ORB) {
int id = PacketUtil.readVarInt(input);
clientEntityTypes.put(id, EntityType.EXPERIENCE_ORB);
PacketUtil.writeVarInt(id, output);
double x = input.readInt();
@ -267,13 +303,44 @@ public class OutgoingTransformer {
return;
}
if (packet == PacketType.PLAY_SPAWN_PAINTING) {
int id = PacketUtil.readVarInt(input);
clientEntityTypes.put(id, EntityType.PAINTING);
PacketUtil.writeVarInt(id, output);
PacketUtil.writeUUID(getUUID(id), output);
String title = PacketUtil.readString(input);
PacketUtil.writeString(title, output);
long[] position = PacketUtil.readBlockPosition(input);
PacketUtil.writeBlockPosition(output, position[0], position[1], position[2]);
byte direction = input.readByte();
output.writeByte(direction);
return;
}
if (packet == PacketType.PLAY_OPEN_WINDOW) {
int windowId = input.readUnsignedByte();
String type = readString(input);
String windowTitle = readString(input);
output.writeByte(windowId);
writeString(type, output);
writeString(fixJson(windowTitle), output);
output.writeBytes(input);
return;
}
if (packet == PacketType.PLAY_SPAWN_MOB) {
int id = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(id, output);
PacketUtil.writeUUID(getUUID(id), output);
short type = input.readUnsignedByte();
clientEntityTypes.put(id, EntityUtil.getTypeFromID(type, false));
output.writeByte(type);
double x = input.readInt();
output.writeDouble(x / 32D);
double y = input.readInt();
@ -295,7 +362,7 @@ public class OutgoingTransformer {
output.writeShort(vZ);
try {
Object dataWatcher = ReflectionUtil.get(info.getLastPacket(), "l", ReflectionUtil.nms("DataWatcher"));
transformMetadata(dataWatcher, output);
transformMetadata(id, dataWatcher, output);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
@ -310,21 +377,28 @@ public class OutgoingTransformer {
output.writeLong(location);
for (int i = 0; i < 4; i++) {
String line = PacketUtil.readString(input);
if (line == null || line.equalsIgnoreCase("null")) {
line = "{\"text\":\"\"}";
} else {
if (!line.startsWith("\"") && !line.startsWith("{"))
line = "\"" + line + "\"";
if (line.startsWith("\""))
line = "{\"text\":" + line + "}";
}
PacketUtil.writeString(line, output);
PacketUtil.writeString(fixJson(line), output);
}
}
if (packet == PacketType.PLAY_CHAT_MESSAGE) {
String chat = PacketUtil.readString(input);
PacketUtil.writeString(fixJson(chat), output);
byte pos = input.readByte();
output.writeByte(pos);
return;
}
if (packet == PacketType.PLAY_JOIN_GAME) {
int id = input.readInt();
clientEntityTypes.put(id, EntityType.PLAYER);
output.writeInt(id);
output.writeBytes(input);
return;
}
if (packet == PacketType.PLAY_SPAWN_PLAYER) {
int id = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(id, output);
clientEntityTypes.put(id, EntityType.PLAYER);
UUID playerUUID = PacketUtil.readUUID(input);
PacketUtil.writeUUID(playerUUID, output);
@ -341,7 +415,7 @@ public class OutgoingTransformer {
output.writeByte(yaw);
try {
Object dataWatcher = ReflectionUtil.get(info.getLastPacket(), "i", ReflectionUtil.nms("DataWatcher"));
transformMetadata(dataWatcher, output);
transformMetadata(id, dataWatcher, output);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
@ -352,7 +426,7 @@ public class OutgoingTransformer {
return;
}
if(packet == PacketType.PLAY_MAP) {
if (packet == PacketType.PLAY_MAP) {
int damage = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(damage, output);
byte scale = input.readByte();
@ -430,30 +504,37 @@ public class OutgoingTransformer {
output.writeBytes(input);
}
private void transformMetadata(Object dw, ByteBuf output) {
private String fixJson(String line) {
if (line == null || line.equalsIgnoreCase("null")) {
line = "{\"text\":\"\"}";
} else {
if (!line.startsWith("\"") && !line.startsWith("{"))
line = "\"" + line + "\"";
if (line.startsWith("\""))
line = "{\"text\":" + line + "}";
}
return line;
}
private void transformMetadata(int entityID, Object dw, ByteBuf output) {
// get entity
try {
Class<?> nmsClass = ReflectionUtil.nms("Entity");
Object nmsEntity = ReflectionUtil.get(dw, "a", nmsClass);
Class<?> craftClass = ReflectionUtil.obc("entity.CraftEntity");
Method bukkitMethod = craftClass.getDeclaredMethod("getEntity", ReflectionUtil.obc("CraftServer"), nmsClass);
Object entity = bukkitMethod.invoke(null, Bukkit.getServer(), nmsEntity);
transformMetadata((Entity) entity, (List) ReflectionUtil.invoke(dw, "b"), output);
} catch (NoSuchFieldException e) {
e.printStackTrace();
transformMetadata(entityID, (List) ReflectionUtil.invoke(dw, "b"), output);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private void transformMetadata(Entity entity, List dw, ByteBuf output) {
private void transformMetadata(int entityID, List dw, ByteBuf output) {
EntityType type = clientEntityTypes.get(entityID);
if (type == null) {
System.out.println("Unable to get entity for ID: " + entityID);
return;
}
if (dw != null) {
short id = -1;
int data = -1;
@ -463,7 +544,7 @@ public class OutgoingTransformer {
Object watchableObj = iterator.next(); //
MetaIndex metaIndex = null;
try {
metaIndex = MetaIndex.getIndex(entity, (int) ReflectionUtil.invoke(watchableObj, "a"));
metaIndex = MetaIndex.getIndex(type, (int) ReflectionUtil.invoke(watchableObj, "a"));
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
@ -553,8 +634,8 @@ public class OutgoingTransformer {
}
} catch (Exception e) {
if (entity != null) {
System.out.println("An error occurred with entity meta data for " + entity.getType());
if (type != null) {
System.out.println("An error occurred with entity meta data for " + type);
System.out.println("Old ID: " + metaIndex.getIndex() + " New ID: " + metaIndex.getNewIndex());
System.out.println("Old Type: " + metaIndex.getOldType() + " New Type: " + metaIndex.getNewType());
}

View File

@ -0,0 +1,91 @@
package us.myles.ViaVersion.util;
import org.bukkit.entity.EntityType;
public class EntityUtil {
public static EntityType getTypeFromID(int typeID, boolean isObject) {
if (isObject) {
return getObjectFromID(typeID);
} else {
return EntityType.fromId(typeID);
}
}
// based on http://wiki.vg/index.php?title=Entities
public static EntityType getObjectFromID(int objectID) {
EntityType type;
switch (objectID) {
case 2:
type = EntityType.DROPPED_ITEM;
break;
case 77:
type = EntityType.LEASH_HITCH;
break;
case 60:
type = EntityType.ARROW;
break;
case 61:
type = EntityType.SNOWBALL;
break;
case 63:
type = EntityType.FIREBALL;
break;
case 64:
type = EntityType.SMALL_FIREBALL;
break;
case 65:
type = EntityType.ENDER_PEARL;
break;
case 72:
type = EntityType.ENDER_SIGNAL;
break;
case 75:
type = EntityType.THROWN_EXP_BOTTLE;
break;
case 71:
type = EntityType.ITEM_FRAME;
break;
case 66:
type = EntityType.WITHER_SKULL;
break;
case 50:
type = EntityType.PRIMED_TNT;
break;
case 70:
type = EntityType.FALLING_BLOCK;
break;
case 76:
type = EntityType.FIREWORK;
break;
case 78:
type = EntityType.ARMOR_STAND;
break;
case 1:
type = EntityType.BOAT;
break;
case 10:
type = EntityType.MINECART;
break;
case 51:
type = EntityType.ENDER_CRYSTAL;
break;
case 73:
type = EntityType.SPLASH_POTION;
break;
case 62:
type = EntityType.EGG;
break;
case 90:
type = EntityType.FISHING_HOOK;
break;
default:
type = EntityType.fromId(objectID);
if (type == null) {
System.out.println("Unable to find entity type for " + objectID);
type = EntityType.UNKNOWN;
}
break;
}
return type;
}
}

View File

@ -1,4 +1,4 @@
package us.myles.ViaVersion;
package us.myles.ViaVersion.util;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
@ -363,4 +363,26 @@ public class PacketUtil {
e.printStackTrace();
}
}
public static long[] readBlockPosition(ByteBuf buf) {
long val = buf.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 long[]{x, y, z};
}
public static void writeBlockPosition(ByteBuf buf, long x, long y, long z) {
buf.writeLong(((x & 0x3ffffff) << 38) | ((y & 0xfff) << 26) | (z & 0x3ffffff));
}
public static int[] readVarInts(int amount, ByteBuf input) {
int data[] = new int[amount];
for (int index = 0; index < amount; index++) {
data[index] = PacketUtil.readVarInt(input);
}
return data;
}
}

View File

@ -1,4 +1,4 @@
package us.myles.ViaVersion;
package us.myles.ViaVersion.util;
import org.bukkit.Bukkit;

View File

@ -1,4 +1,4 @@
name: ViaVersion
main: us.myles.ViaVersion.Core
main: us.myles.ViaVersion.ViaVersionPlugin
author: _MylesC
version: 0.3.3
version: 0.3.7