ViaVersion/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java

485 lines
19 KiB
Java

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 net.minecraft.server.v1_8_R3.*;
import net.minecraft.server.v1_8_R3.ItemStack;
import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack;
import org.bukkit.inventory.*;
import org.spacehq.mc.protocol.data.game.chunk.Column;
import org.spacehq.mc.protocol.util.NetUtil;
import us.myles.ViaVersion.CancelException;
import us.myles.ViaVersion.ConnectionInfo;
import us.myles.ViaVersion.Core;
import us.myles.ViaVersion.PacketUtil;
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
import us.myles.ViaVersion.metadata.MetaIndex;
import us.myles.ViaVersion.metadata.NewType;
import us.myles.ViaVersion.metadata.Type;
import us.myles.ViaVersion.packets.PacketType;
import us.myles.ViaVersion.packets.State;
import java.io.IOException;
import java.util.*;
public class OutgoingTransformer {
private static Gson gson = new Gson();
private final Channel channel;
private final ConnectionInfo info;
private final ViaVersionInitializer init;
private boolean cancel = false;
private Map<Integer, UUID> uuidMap = new HashMap<Integer, UUID>();
public OutgoingTransformer(Channel channel, ConnectionInfo info, ViaVersionInitializer init) {
this.channel = channel;
this.info = info;
this.init = init;
}
public void transform(int packetID, ByteBuf input, ByteBuf output) throws CancelException {
if (cancel) {
throw new CancelException();
}
PacketType packet = PacketType.getOutgoingPacket(info.getState(), packetID);
if (packet == null) {
throw new RuntimeException("Outgoing Packet not found? " + packetID + " State: " + info.getState() + " Version: " + info.getProtocol());
}
if (packet != PacketType.PLAY_CHUNK_DATA && packet != PacketType.PLAY_KEEP_ALIVE && packet != PacketType.PLAY_TIME_UPDATE)
System.out.println("Packet Type: " + packet + " Original ID: " + packetID + " State:" + info.getState());
if (packet.getPacketID() != -1) {
packetID = packet.getNewPacketID();
}
// By default no transform
PacketUtil.writeVarInt(packetID, output);
if (packet == PacketType.PLAY_NAMED_SOUND_EFFECT) {
// TODO: Port this over
System.out.println("cancelling");
throw new CancelException();
}
if (packet == PacketType.PLAY_ATTACH_ENTITY) {
int id = input.readInt();
output.writeInt(id);
int target = input.readInt();
output.writeInt(target);
return;
}
if (packet == PacketType.PLAY_ENTITY_TELEPORT) {
// Port this so that it relative moves :P
int id = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(id, output);
int x = input.readInt();
output.writeDouble(x / 32D);
int y = input.readInt();
output.writeDouble(y / 32D);
int z = input.readInt();
output.writeDouble(z / 32D);
byte yaw = input.readByte();
output.writeByte(yaw);
byte pitch = input.readByte();
output.writeByte(pitch);
boolean onGround = input.readBoolean();
output.writeBoolean(onGround);
return;
}
if (packet == PacketType.PLAY_ENTITY_LOOK_MOVE) {
int id = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(id, output);
int x = input.readByte();
output.writeShort(x);
int y = input.readByte();
output.writeShort(y);
int z = input.readByte();
output.writeShort(z);
byte yaw = input.readByte();
output.writeByte(yaw);
byte pitch = input.readByte();
output.writeByte(pitch);
boolean onGround = input.readBoolean();
output.writeBoolean(onGround);
return;
}
if (packet == PacketType.PLAY_ENTITY_RELATIVE_MOVE) {
int id = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(id, output);
int x = input.readByte();
output.writeShort(x);
int y = input.readByte();
output.writeShort(y);
int z = input.readByte();
output.writeShort(z);
boolean onGround = input.readBoolean();
output.writeBoolean(onGround);
return;
}
// If login success
if (packet == PacketType.LOGIN_SUCCESS) {
info.setState(State.PLAY);
}
if (packet == PacketType.LOGIN_SETCOMPRESSION) {
int factor = PacketUtil.readVarInt(input);
info.setCompression(factor);
PacketUtil.writeVarInt(factor, output);
return;
}
if (packet == PacketType.STATUS_RESPONSE) {
String original = PacketUtil.readString(input);
JsonObject object = gson.fromJson(original, JsonObject.class);
object.get("version").getAsJsonObject().addProperty("protocol", info.getProtocol());
PacketUtil.writeString(gson.toJson(object), output);
return;
}
if (packet == PacketType.LOGIN_SUCCESS) {
String uu = PacketUtil.readString(input);
PacketUtil.writeString(uu, output);
info.setUUID(UUID.fromString(uu));
output.writeBytes(input);
return;
}
if (packet == PacketType.PLAY_PLAYER_POSITION_LOOK) {
output.writeBytes(input);
PacketUtil.writeVarInt(0, output);
return;
}
if (packet == PacketType.PLAY_ENTITY_EQUIPMENT) {
int id = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(id, output);
short slot = input.readShort();
if (slot > 1) {
slot += 1; // add 1 so it's now 2-5
}
PacketUtil.writeVarInt(slot, output);
output.writeBytes(input);
}
if (packet == PacketType.PLAY_ENTITY_METADATA) {
int id = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(id, output);
try {
List dw = Core.getPrivateField(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();
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
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();
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);
byte pitch = input.readByte();
output.writeByte(pitch);
byte yaw = input.readByte();
output.writeByte(yaw);
int data = input.readInt();
output.writeInt(data);
short vX = input.readableBytes() >= 16 ? input.readShort() : 0;
output.writeShort(vX);
short vY = input.readableBytes() >= 16 ? input.readShort() : 0;
output.writeShort(vY);
short vZ = input.readableBytes() >= 16 ? input.readShort() : 0;
output.writeShort(vZ);
return;
}
if (packet == PacketType.PLAY_SPAWN_XP_ORB) { // TODO: Verify
int id = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(id, output);
double x = input.readInt();
output.writeDouble(x / 32D);
double y = input.readInt();
output.writeDouble(y / 32D);
double z = input.readInt();
output.writeDouble(z / 32D);
short data = input.readShort();
output.writeShort(data);
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();
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);
byte yaw = input.readByte();
output.writeByte(yaw);
byte pitch = input.readByte();
output.writeByte(pitch);
byte headPitch = input.readByte();
output.writeByte(headPitch);
short vX = input.readShort();
output.writeShort(vX);
short vY = input.readShort();
output.writeShort(vY);
short vZ = input.readShort();
output.writeShort(vZ);
try {
DataWatcher dw = Core.getPrivateField(info.getLastPacket(), "l", DataWatcher.class);
transformMetadata(dw, output);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return;
}
if (packet == PacketType.PLAY_SPAWN_PLAYER) {
int id = PacketUtil.readVarInt(input);
PacketUtil.writeVarInt(id, output);
UUID playerUUID = PacketUtil.readUUID(input);
PacketUtil.writeUUID(playerUUID, output);
double x = input.readInt();
output.writeDouble(x / 32D);
double y = input.readInt();
output.writeDouble(y / 32D);
double z = input.readInt();
output.writeDouble(z / 32D);
byte pitch = input.readByte();
output.writeByte(pitch);
byte yaw = input.readByte();
output.writeByte(yaw);
// We don't use currentItem short lel
// transform entity meta data ugh
// get data watcher
try {
System.out.println("Last Packet Type: " + info.getLastPacket().getClass().getName());
DataWatcher dw = Core.getPrivateField(info.getLastPacket(), "i", DataWatcher.class);
transformMetadata(dw, output);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return;
}
if (packet == PacketType.PLAY_CHUNK_DATA) {
// We need to catch unloading chunk packets as defined by wiki.vg
// To unload chunks, send this packet with Ground-Up Continuous=true and no 16^3 chunks (eg. Primary Bit Mask=0)
int chunkX = input.readInt();
int chunkZ = input.readInt();
output.writeInt(chunkX);
output.writeInt(chunkZ);
boolean groundUp = input.readBoolean();
output.writeBoolean(groundUp);
int bitMask = input.readUnsignedShort();
if (bitMask == 0) {
output.clear();
PacketUtil.writeVarInt(PacketType.PLAY_UNLOAD_CHUNK.getNewPacketID(), output);
output.writeInt(chunkX);
output.writeInt(chunkZ);
return;
}
int size = PacketUtil.readVarInt(input);
byte[] data = new byte[size];
input.readBytes(data);
boolean sk = false;
if (info.getLastPacket() instanceof PacketPlayOutMapChunkBulk) {
try {
sk = Core.getPrivateField(info.getLastPacket(), "d", boolean.class);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
Column read = NetUtil.readOldChunkData(chunkX, chunkZ, groundUp, bitMask, data, true, sk);
// Write chunk section array :((
ByteBuf temp = output.alloc().buffer();
try {
int bitmask = NetUtil.writeNewColumn(temp, read, groundUp, sk);
PacketUtil.writeVarInt(bitmask, output);
PacketUtil.writeVarInt(temp.readableBytes(), output);
output.writeBytes(temp);
} catch (IOException e) {
e.printStackTrace();
}
return;
}
output.writeBytes(input);
}
private void transformMetadata(DataWatcher dw, ByteBuf output) {
// get entity
try {
transformMetadata(Core.getPrivateField(dw, "a", Entity.class), dw.b(), output);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private void transformMetadata(Entity entity, List<DataWatcher.WatchableObject> dw, ByteBuf output) {
PacketDataSerializer packetdataserializer = new PacketDataSerializer(output);
if (dw != null) {
short id = -1;
int data = -1;
Iterator<DataWatcher.WatchableObject> iterator = dw.iterator();
while (iterator.hasNext()) {
DataWatcher.WatchableObject obj = iterator.next();
MetaIndex metaIndex = MetaIndex.getIndex(entity, obj.a());
if (metaIndex.getNewType() != NewType.Discontinued) {
if (metaIndex.getNewType() != NewType.BlockID || id != -1 && data == -1 || id == -1 && data != -1) { // block ID is only written if we have both parts
output.writeByte(metaIndex.getNewIndex());
output.writeByte(metaIndex.getNewType().getTypeID());
}
switch (metaIndex.getNewType()) {
case Byte:
// convert from int, byte
if (metaIndex.getOldType() == Type.Byte) {
packetdataserializer.writeByte(((Byte) obj.b()).byteValue());
}
if (metaIndex.getOldType() == Type.Int) {
packetdataserializer.writeByte(((Integer) obj.b()).byteValue());
}
break;
case OptUUID:
String owner = (String) obj.b();
UUID toWrite = null;
if (owner.length() != 0) {
try {
toWrite = UUID.fromString(owner);
} catch (Exception ignored) {
}
}
packetdataserializer.writeBoolean(toWrite != null);
if (toWrite != null)
packetdataserializer.a(toWrite);
break;
case BlockID:
// if we have both sources :))
if (metaIndex.getOldType() == Type.Byte) {
data = ((Byte) obj.b()).byteValue();
}
if (metaIndex.getOldType() == Type.Short) {
id = ((Short) obj.b()).shortValue();
}
if (id != -1 && data != -1) {
int combined = id << 4 | data;
data = -1;
id = -1;
PacketUtil.writeVarInt(combined, output);
}
break;
case VarInt:
// convert from int, short, byte
if (metaIndex.getOldType() == Type.Byte) {
PacketUtil.writeVarInt(((Byte) obj.b()).intValue(), output);
}
if (metaIndex.getOldType() == Type.Short) {
PacketUtil.writeVarInt(((Short) obj.b()).intValue(), output);
}
if (metaIndex.getOldType() == Type.Int) {
PacketUtil.writeVarInt(((Integer) obj.b()).intValue(), output);
}
break;
case Float:
packetdataserializer.writeFloat(((Float) obj.b()).floatValue());
break;
case String:
packetdataserializer.a((String) obj.b());
break;
case Boolean:
packetdataserializer.writeBoolean(((Byte) obj.b()).byteValue() != 0);
break;
case Slot:
ItemStack itemstack = (ItemStack) obj.b();
packetdataserializer.a(itemstack);
break;
case Position:
BlockPosition blockposition = (BlockPosition) obj.b();
packetdataserializer.writeInt(blockposition.getX());
packetdataserializer.writeInt(blockposition.getY());
packetdataserializer.writeInt(blockposition.getZ());
break;
case Vector3F:
Vector3f vector3f = (Vector3f) obj.b();
packetdataserializer.writeFloat(vector3f.getX());
packetdataserializer.writeFloat(vector3f.getY());
packetdataserializer.writeFloat(vector3f.getZ());
}
}
}
}
output.writeByte(255);
}
private UUID getUUID(int id) {
if (uuidMap.containsKey(id)) {
return uuidMap.get(id);
} else {
UUID uuid = UUID.randomUUID();
uuidMap.put(id, uuid);
return uuid;
}
}
}