blob: c62caef672495c59815e26a29848b0ed610975e2 [file] [log] [blame] [raw]
package protocolsupport.protocol.serializer;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.text.MessageFormat;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.EncoderException;
import protocolsupport.api.ProtocolType;
import protocolsupport.api.ProtocolVersion;
import protocolsupport.api.chat.ChatAPI;
import protocolsupport.api.chat.components.BaseComponent;
import protocolsupport.api.events.ItemStackWriteEvent;
import protocolsupport.protocol.typeremapper.itemstack.ItemStackRemapper;
import protocolsupport.protocol.utils.CommonNBT;
import protocolsupport.protocol.utils.types.NetworkItemStack;
import protocolsupport.protocol.utils.types.nbt.NBTCompound;
import protocolsupport.protocol.utils.types.nbt.NBTEnd;
import protocolsupport.protocol.utils.types.nbt.NBTList;
import protocolsupport.protocol.utils.types.nbt.NBTString;
import protocolsupport.protocol.utils.types.nbt.NBTType;
import protocolsupport.protocol.utils.types.nbt.serializer.DefaultNBTSerializer;
import protocolsupport.zplatform.ServerPlatform;
public class ItemStackSerializer {
public static NetworkItemStack readItemStack(ByteBuf from, ProtocolVersion version, String locale, boolean isFromClient) {
int type;
if (version.isAfterOrEq(ProtocolVersion.MINECRAFT_1_13_2)) {
boolean exist = from.readBoolean();
if (!exist) {
return NetworkItemStack.NULL;
}
type = VarNumberSerializer.readVarInt(from);
} else {
type = from.readShort();
if (type < 0) {
return NetworkItemStack.NULL;
}
}
NetworkItemStack itemstack = new NetworkItemStack();
itemstack.setTypeId(type);
itemstack.setAmount(from.readByte());
if (version.isBefore(ProtocolVersion.MINECRAFT_1_13)) {
itemstack.setLegacyData(from.readUnsignedShort());
}
itemstack.setNBT(readTag(from, version));
if (isFromClient) {
itemstack = ItemStackRemapper.remapFromClient(version, locale, itemstack);
}
return itemstack;
}
public static void writeItemStack(ByteBuf to, ProtocolVersion version, String locale, NetworkItemStack itemstack, boolean isToClient) {
if (itemstack.isNull()) {
if (version.isAfterOrEq(ProtocolVersion.MINECRAFT_1_13_2)) {
to.writeBoolean(false);
} else {
to.writeShort(-1);
}
return;
}
if (isToClient) {
if (ItemStackWriteEvent.getHandlerList().getRegisteredListeners().length > 0) {
ItemStack bukkitStack = ServerPlatform.get().getMiscUtils().createItemStackFromNetwork(itemstack);
ItemStackWriteEvent event = new ItemStackWriteEvent(version, locale, bukkitStack);
Bukkit.getPluginManager().callEvent(event);
List<String> additionalLore = event.getAdditionalLore();
BaseComponent forcedDisplayName = event.getForcedDisplayName();
if ((forcedDisplayName != null) || !additionalLore.isEmpty()) {
NBTCompound nbt = itemstack.getNBT();
if (nbt == null) {
nbt = new NBTCompound();
}
NBTCompound displayNBT = CommonNBT.getOrCreateDisplayTag(nbt);
if (forcedDisplayName != null) {
displayNBT.setTag(CommonNBT.DISPLAY_NAME, new NBTString(ChatAPI.toJSON(forcedDisplayName)));
}
if (!additionalLore.isEmpty()) {
NBTList<NBTString> loreNBT = displayNBT.getTagListOfType(CommonNBT.DISPLAY_LORE, NBTType.STRING);
if (loreNBT == null) {
loreNBT = new NBTList<>(NBTType.STRING);
}
for (String lore : additionalLore) {
loreNBT.addTag(new NBTString(lore));
}
displayNBT.setTag(CommonNBT.DISPLAY_LORE, loreNBT);
}
itemstack.setNBT(nbt);
}
}
itemstack = ItemStackRemapper.remapToClient(version, locale, itemstack);
}
if (version.isAfterOrEq(ProtocolVersion.MINECRAFT_1_13_2)) {
to.writeBoolean(true);
VarNumberSerializer.writeVarInt(to, itemstack.getTypeId());
} else {
to.writeShort(itemstack.getTypeId());
}
to.writeByte(itemstack.getAmount());
if (version.isBefore(ProtocolVersion.MINECRAFT_1_13)) {
to.writeShort(itemstack.getLegacyData());
}
writeTag(to, version, itemstack.getNBT());
}
public static NBTCompound readTag(ByteBuf from, ProtocolVersion version) {
try {
if (isUsingShortLengthNBT(version)) {
final short length = from.readShort();
if (length < 0) {
return null;
}
try (DataInputStream stream = new DataInputStream(new GZIPInputStream(new ByteBufInputStream(from.readSlice(length))))) {
return DefaultNBTSerializer.INSTANCE.deserializeTag(stream);
}
} else if (isUsingDirectNBT(version)) {
return DefaultNBTSerializer.INSTANCE.deserializeTag(new ByteBufInputStream(from));
} else {
throw new IllegalArgumentException(MessageFormat.format("Dont know how to read nbt of version {0}", version));
}
} catch (Exception e) {
throw new DecoderException(e);
}
}
public static void writeTag(ByteBuf to, ProtocolVersion version, NBTCompound tag) {
if (isUsingShortLengthNBT(version)) {
if (tag == null) {
to.writeShort(-1);
} else {
MiscSerializer.writeLengthPrefixedBytes(
to,
(lTo, length) -> lTo.writeShort(length),
lTo -> {
try (DataOutputStream outputstream = new DataOutputStream(new GZIPOutputStream(new ByteBufOutputStream(lTo)))) {
DefaultNBTSerializer.INSTANCE.serializeTag(outputstream, tag);
} catch (Exception e) {
throw new EncoderException(e);
}
}
);
}
} else if (isUsingDirectNBT(version)) {
try (ByteBufOutputStream outputstream = new ByteBufOutputStream(to)) {
if (tag != null) {
DefaultNBTSerializer.INSTANCE.serializeTag(outputstream, tag);
} else {
DefaultNBTSerializer.INSTANCE.serializeTag(outputstream, NBTEnd.INSTANCE);
}
} catch (Exception e) {
throw new EncoderException(e);
}
} else {
throw new IllegalArgumentException(MessageFormat.format("Dont know how to write nbt of version {0}", version));
}
}
private static final boolean isUsingShortLengthNBT(ProtocolVersion version) {
return (version.getProtocolType() == ProtocolType.PC) && version.isBeforeOrEq(ProtocolVersion.MINECRAFT_1_7_10);
}
private static final boolean isUsingDirectNBT(ProtocolVersion version) {
return (version.getProtocolType() == ProtocolType.PC) && version.isAfterOrEq(ProtocolVersion.MINECRAFT_1_8);
}
}