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; | |
} | |
} | |
} |