blob: fd27f7b362a9b6f01748ce7e5643eceecfbe253f [file] [log] [blame] [raw]
package protocolsupport.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.handler.codec.EncoderException;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import net.minecraft.server.v1_8_R3.GameProfileSerializer;
import net.minecraft.server.v1_8_R3.IChatBaseComponent.ChatSerializer;
import net.minecraft.server.v1_8_R3.Enchantment;
import net.minecraft.server.v1_8_R3.Item;
import net.minecraft.server.v1_8_R3.ItemStack;
import net.minecraft.server.v1_8_R3.Items;
import net.minecraft.server.v1_8_R3.NBTCompressedStreamTools;
import net.minecraft.server.v1_8_R3.NBTReadLimiter;
import net.minecraft.server.v1_8_R3.NBTTagCompound;
import net.minecraft.server.v1_8_R3.NBTTagList;
import net.minecraft.server.v1_8_R3.NBTTagString;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack;
import org.spigotmc.LimitStream;
import org.spigotmc.SneakyThrow;
import protocolsupport.api.ProtocolVersion;
import protocolsupport.api.events.ItemStackWriteEvent;
import protocolsupport.protocol.transformer.utils.LegacyUtils;
import protocolsupport.protocol.typeremapper.id.IdRemapper;
import protocolsupport.utils.Allocator;
import protocolsupport.utils.Utils;
import com.mojang.authlib.GameProfile;
public class PacketDataSerializer extends net.minecraft.server.v1_8_R3.PacketDataSerializer {
private ProtocolVersion version;
public PacketDataSerializer(ByteBuf buf, ProtocolVersion version) {
super(buf);
this.version = version;
}
public ProtocolVersion getVersion() {
return version;
}
public void setVersion(ProtocolVersion version) {
this.version = version;
}
@Override
public void a(ItemStack itemstack) {
itemstack = transformItemStack(itemstack);
if ((itemstack == null) || (itemstack.getItem() == null)) {
writeShort(-1);
} else {
int itemId = Item.getId(itemstack.getItem());
writeShort(IdRemapper.ITEM.getTable(getVersion()).getRemap(itemId));
writeByte(itemstack.count);
writeShort(itemstack.getData());
a(itemstack.getTag());
}
}
@Override
public void a(NBTTagCompound nbttagcompound) {
if (getVersion() != ProtocolVersion.MINECRAFT_1_8) {
if (nbttagcompound == null) {
writeShort(-1);
} else {
byte[] abyte = write(nbttagcompound);
writeShort(abyte.length);
writeBytes(abyte);
}
} else {
if (nbttagcompound == null) {
writeByte(0);
} else {
ByteBufOutputStream out = new ByteBufOutputStream(Allocator.allocateBuffer());
try {
NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) new DataOutputStream(out));
writeBytes(out.buffer());
} catch (Throwable ioexception) {
throw new EncoderException(ioexception);
} finally {
out.buffer().release();
}
}
}
}
private ItemStack transformItemStack(ItemStack original) {
if (original == null) {
return null;
}
ItemStack itemstack = original.cloneItemStack();
Item item = itemstack.getItem();
NBTTagCompound nbttagcompound = itemstack.getTag();
if (nbttagcompound != null) {
if (getVersion() != ProtocolVersion.MINECRAFT_1_8 && item == Items.WRITTEN_BOOK) {
if (nbttagcompound.hasKeyOfType("pages", 9)) {
NBTTagList pages = nbttagcompound.getList("pages", 8);
NBTTagList newpages = new NBTTagList();
for (int i = 0; i < pages.size(); i++) {
newpages.add(new NBTTagString(LegacyUtils.fromComponent(ChatSerializer.a(pages.getString(i)))));
}
nbttagcompound.set("pages", newpages);
}
}
switch (getVersion()) {
case MINECRAFT_1_7_5:
case MINECRAFT_1_6_4:
case MINECRAFT_1_6_2:
case MINECRAFT_1_5_2: {
transformSkull(nbttagcompound, "SkullOwner", "SkullOwner");
transformSkull(nbttagcompound, "Owner", "ExtraType");
break;
}
default: {
break;
}
}
if (getVersion() != ProtocolVersion.MINECRAFT_1_8 && nbttagcompound.hasKeyOfType("ench", 9)) {
NBTTagList enchList = nbttagcompound.getList("ench", 10);
NBTTagList newList = new NBTTagList();
for (int i = 0; i < enchList.size(); i++) {
NBTTagCompound enchData = enchList.get(i);
if ((enchData.getInt("id") & 0xFFFF) != Enchantment.DEPTH_STRIDER.id) {
newList.add(enchData);
}
}
nbttagcompound.set("ench", newList);
}
}
ItemStackWriteEvent event = new InternalItemStackWriteEvent(original, itemstack);
Bukkit.getPluginManager().callEvent(event);
return itemstack;
}
private void transformSkull(NBTTagCompound tag, String tagname, String newtagname) {
if (tag.hasKeyOfType(tagname, 10)) {
GameProfile gameprofile = GameProfileSerializer.deserialize(tag.getCompound(tagname));
if (gameprofile.getName() != null) {
tag.set(newtagname, new NBTTagString(gameprofile.getName()));
} else {
tag.remove(tagname);
}
}
}
@Override
public NBTTagCompound h() throws IOException {
if (getVersion() != ProtocolVersion.MINECRAFT_1_8) {
final short length = readShort();
if (length < 0) {
return null;
}
final byte[] data = new byte[length];
readBytes(data);
return read(data, new NBTReadLimiter(2097152L));
} else {
int index = readerIndex();
if (readByte() == 0) {
return null;
}
readerIndex(index);
return NBTCompressedStreamTools.a(new DataInputStream(new ByteBufInputStream(this)), new NBTReadLimiter(2097152L));
}
}
@Override
public String c(int limit) {
switch (getVersion()) {
case MINECRAFT_1_6_4:
case MINECRAFT_1_6_2:
case MINECRAFT_1_5_2: {
return new String(Utils.toArray(readBytes(readUnsignedShort() * 2)), StandardCharsets.UTF_16BE);
}
default: {
return super.c(limit);
}
}
}
@Override
public PacketDataSerializer a(String string) {
switch (getVersion()) {
case MINECRAFT_1_6_4:
case MINECRAFT_1_6_2:
case MINECRAFT_1_5_2: {
writeShort(string.length());
writeBytes(string.getBytes(StandardCharsets.UTF_16BE));
break;
}
default: {
super.a(string);
break;
}
}
return this;
}
@Override
public byte[] a() {
switch (getVersion()) {
case MINECRAFT_1_7_10:
case MINECRAFT_1_7_5:
case MINECRAFT_1_6_4:
case MINECRAFT_1_6_2:
case MINECRAFT_1_5_2: {
byte[] array = new byte[readShort()];
readBytes(array);
return array;
}
default: {
return super.a();
}
}
}
@Override
public void a(byte[] array) {
switch (getVersion()) {
case MINECRAFT_1_7_10:
case MINECRAFT_1_7_5:
case MINECRAFT_1_6_4:
case MINECRAFT_1_6_2:
case MINECRAFT_1_5_2: {
if (array.length > 32767) {
throw new IllegalArgumentException("Too big array length of "+array.length);
}
writeShort(array.length);
writeBytes(array);
break;
}
default: {
super.a(array);
break;
}
}
}
public int readVarInt() {
return e();
}
public void writeVarInt(int varInt) {
b(varInt);
}
public String readString(int limit) {
return c(limit);
}
public void writeString(String string) {
a(string);
}
public ItemStack readItemStack() throws IOException {
return i();
}
public void writeItemStack(ItemStack itemstack) {
a(itemstack);
}
public byte[] readArray() {
return a();
}
private static NBTTagCompound read(final byte[] data, final NBTReadLimiter nbtreadlimiter) {
try {
try (DataInputStream datainputstream = new DataInputStream(new BufferedInputStream(new LimitStream(new GZIPInputStream(new ByteArrayInputStream(data)), nbtreadlimiter)))) {
return NBTCompressedStreamTools.a(datainputstream, nbtreadlimiter);
}
} catch (IOException ex) {
SneakyThrow.sneaky(ex);
return null;
}
}
private static byte[] write(final NBTTagCompound nbttagcompound) {
try {
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
try (DataOutputStream dataoutputstream = new DataOutputStream(new GZIPOutputStream(bytearrayoutputstream))) {
NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream);
}
return bytearrayoutputstream.toByteArray();
} catch (IOException ex) {
SneakyThrow.sneaky(ex);
return null;
}
}
public static class InternalItemStackWriteEvent extends ItemStackWriteEvent {
private final CraftItemStack wrapped;
public InternalItemStackWriteEvent(ItemStack original, ItemStack itemstack) {
super(CraftItemStack.asCraftMirror(original));
this.wrapped = CraftItemStack.asCraftMirror(itemstack);
}
@Override
public org.bukkit.inventory.ItemStack getResult() {
return wrapped;
}
}
}