| package us.myles.ViaVersion.metadata; | |
| import io.netty.buffer.ByteBuf; | |
| import lombok.Getter; | |
| import lombok.Setter; | |
| import org.bukkit.entity.EntityType; | |
| import org.bukkit.util.EulerAngle; | |
| import org.bukkit.util.Vector; | |
| import us.myles.ViaVersion.ViaVersionPlugin; | |
| import us.myles.ViaVersion.api.ViaVersion; | |
| import us.myles.ViaVersion.slot.ItemSlotRewriter; | |
| import us.myles.ViaVersion.slot.ItemSlotRewriter.ItemStack; | |
| import us.myles.ViaVersion.transformers.OutgoingTransformer; | |
| import us.myles.ViaVersion.util.PacketUtil; | |
| import java.util.ArrayList; | |
| import java.util.List; | |
| import java.util.UUID; | |
| public class MetadataRewriter { | |
| public static void writeMetadata1_9(EntityType type, List<Entry> list, ByteBuf output) { | |
| short id = -1; | |
| int data = -1; | |
| for (Entry entry : list) { | |
| MetaIndex metaIndex = entry.index; | |
| try { | |
| 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()); | |
| } | |
| Object value = entry.value; | |
| switch (metaIndex.getNewType()) { | |
| case Byte: | |
| // convert from int, byte | |
| if (metaIndex.getOldType() == Type.Byte) { | |
| output.writeByte((Byte) value); | |
| } | |
| if (metaIndex.getOldType() == Type.Int) { | |
| output.writeByte(((Integer) value).byteValue()); | |
| } | |
| // After writing the last one | |
| if (metaIndex == MetaIndex.ENTITY_STATUS && type == EntityType.PLAYER) { | |
| output.writeByte(MetaIndex.PLAYER_HAND.getNewIndex()); | |
| output.writeByte(MetaIndex.PLAYER_HAND.getNewType().getTypeID()); | |
| if ((((Byte) value) & 0x10) == 0x10) { // Player eating/aiming/drinking | |
| output.writeByte(1); // Using main hand | |
| } else { | |
| output.writeByte(0); // Not using any hand to stop animation | |
| } | |
| } | |
| break; | |
| case OptUUID: | |
| String owner = (String) value; | |
| UUID toWrite = null; | |
| if (owner.length() != 0) { | |
| try { | |
| toWrite = UUID.fromString(owner); | |
| } catch (Exception ignored) { | |
| } | |
| } | |
| output.writeBoolean(toWrite != null); | |
| if (toWrite != null) | |
| PacketUtil.writeUUID(toWrite, output); | |
| break; | |
| case BlockID: | |
| // if we have both sources :)) | |
| if (metaIndex.getOldType() == Type.Byte) { | |
| data = (Byte) value; | |
| } | |
| if (metaIndex.getOldType() == Type.Short) { | |
| id = (Short) value; | |
| } | |
| 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) value).intValue(), output); | |
| } | |
| if (metaIndex.getOldType() == Type.Short) { | |
| PacketUtil.writeVarInt(((Short) value).intValue(), output); | |
| } | |
| if (metaIndex.getOldType() == Type.Int) { | |
| PacketUtil.writeVarInt((Integer) value, output); | |
| } | |
| break; | |
| case Float: | |
| output.writeFloat((Float) value); | |
| break; | |
| case String: | |
| PacketUtil.writeString((String) value, output); | |
| break; | |
| case Boolean: | |
| if (metaIndex == MetaIndex.AGEABLE_AGE) | |
| output.writeBoolean((Byte) value < 0); | |
| else | |
| output.writeBoolean((Byte) value != 0); | |
| break; | |
| case Slot: | |
| ItemStack item = (ItemStack) value; | |
| ItemSlotRewriter.fixIdsFrom1_8To1_9(item); | |
| ItemSlotRewriter.writeItemStack(item, output); | |
| break; | |
| case Position: | |
| Vector vector = (Vector) value; | |
| output.writeInt((int) vector.getX()); | |
| output.writeInt((int) vector.getY()); | |
| output.writeInt((int) vector.getZ()); | |
| break; | |
| case Vector3F: | |
| EulerAngle angle = (EulerAngle) value; | |
| output.writeFloat((float) angle.getX()); | |
| output.writeFloat((float) angle.getY()); | |
| output.writeFloat((float) angle.getZ()); | |
| break; | |
| case Chat: | |
| PacketUtil.writeString(OutgoingTransformer.fixJson((String) value), output); | |
| break; | |
| default: | |
| System.out.println("[Out] Unhandled MetaDataType: " + metaIndex.getNewType()); | |
| break; | |
| } | |
| } | |
| } catch (Exception e) { | |
| if (!((ViaVersionPlugin) ViaVersion.getInstance()).isSuppressMetadataErrors()) { | |
| System.out.println("INCLUDE THIS IN YOUR ERROR LOG!"); | |
| if (type != null) | |
| System.out.println("An error occurred with entity meta data for " + type + " OldID: " + entry.oldID); | |
| else | |
| System.out.println("An error occurred with entity meta data for UNKNOWN_ENTITY OldID: " + entry.oldID); | |
| if (metaIndex != null) { | |
| System.out.println("Old ID: " + metaIndex.getIndex() + " New ID: " + metaIndex.getNewIndex()); | |
| System.out.println("Old Type: " + metaIndex.getOldType() + " New Type: " + metaIndex.getNewType()); | |
| } | |
| e.printStackTrace(); | |
| } | |
| } | |
| } | |
| output.writeByte(255); | |
| } | |
| public static List<Entry> readMetadata1_8(EntityType entityType, ByteBuf buf) { | |
| List<Entry> entries = new ArrayList<>(); | |
| byte item; | |
| while ((item = buf.readByte()) != 127) { | |
| Type type = Type.byId((item & 0xE0) >> 5); | |
| int id = item & 0x1F; | |
| MetaIndex index = MetaIndex.getIndex(entityType, id); | |
| switch (type) { | |
| case Byte: | |
| entries.add(new Entry(index, buf.readByte(), id)); | |
| break; | |
| case Short: | |
| entries.add(new Entry(index, buf.readShort(), id)); | |
| break; | |
| case Int: | |
| entries.add(new Entry(index, buf.readInt(), id)); | |
| break; | |
| case Float: | |
| entries.add(new Entry(index, buf.readFloat(), id)); | |
| break; | |
| case String: | |
| entries.add(new Entry(index, PacketUtil.readString(buf), id)); | |
| break; | |
| case Slot: { | |
| try { | |
| entries.add(new Entry(index, ItemSlotRewriter.readItemStack(buf), id)); | |
| } catch (Exception e) { | |
| e.printStackTrace(); | |
| } | |
| } | |
| break; | |
| case Position: { | |
| int x = buf.readInt(); | |
| int y = buf.readInt(); | |
| int z = buf.readInt(); | |
| entries.add(new Entry(index, new Vector(x, y, z), id)); | |
| break; | |
| } | |
| case Rotation: { | |
| float x = buf.readFloat(); | |
| float y = buf.readFloat(); | |
| float z = buf.readFloat(); | |
| entries.add(new Entry(index, new EulerAngle(x, y, z), id)); | |
| break; | |
| } | |
| default: | |
| System.out.println("[Out] Unhandled MetaDataType: " + type); | |
| break; | |
| } | |
| } | |
| return entries; | |
| } | |
| @Getter | |
| @Setter | |
| public static final class Entry { | |
| private final int oldID; | |
| private MetaIndex index; | |
| private Object value; | |
| public Entry(MetaIndex index, Object value, int id) { | |
| this.index = index; | |
| this.value = value; | |
| this.oldID = id; | |
| } | |
| } | |
| } |