package com.captainbern.minecraft.net.util;
import com.captainbern.minecraft.game.BlockVector;
import com.captainbern.minecraft.game.ItemStack;
import com.captainbern.minecraft.game.Position;
import com.captainbern.minecraft.game.Rotation;
import com.captainbern.minecraft.game.chat.Message;
import com.captainbern.minecraft.game.entity.metadata.Metadata;
import com.captainbern.minecraft.game.entity.metadata.MetadataType;
import com.captainbern.minecraft.game.nbt.NbtCompressedStreamTools;
import com.captainbern.minecraft.game.nbt.NbtReadLimiter;
import com.captainbern.minecraft.game.nbt.NbtTagCompound;
import com.captainbern.minecraft.game.nbt.NbtTagType;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class ByteBufUtils {
private ByteBufUtils() {
super();
}
public static UUID readUuid(ByteBuf buf) {
return new UUID(buf.readLong(), buf.readLong());
}
public static void writeUuid(ByteBuf buf, UUID uuid) {
buf.writeLong(uuid.getMostSignificantBits());
buf.writeLong(uuid.getLeastSignificantBits());
}
public static BlockVector readBlockPosition(ByteBuf byteBuf) {
long pos = byteBuf.readLong(); // 64bit long
int x = (int) (pos >> 38);
int y = (int) ((pos >> 26) & 0xFFF);
int z = (int) (pos << 38 >> 38);
return new BlockVector(x, y, z);
}
public static void writeBlockPosition(ByteBuf byteBuf, BlockVector vector) {
writeBlockPosition(byteBuf, vector.getX(), vector.getY(), vector.getZ());
}
public static void writeBlockPosition(ByteBuf buf, long x, long y, long z) {
buf.writeLong(((x & 0x3ffffff) << 38) | ((y & 0xfff) << 26) | (z & 0x3ffffff));
}
public static Message readMessage(ByteBuf byteBuf) {
return Message.fromJsonString(readUTF(byteBuf));
}
public static void writeMessage(ByteBuf byteBuf, Message message) {
writeUTF(byteBuf, message.toJsonString());
}
public static ItemStack readSlot(ByteBuf byteBuf) {
short item = byteBuf.readShort();
if (item == -1)
return null;
int amount = byteBuf.readUnsignedByte();
short durability = byteBuf.readShort();
NbtTagCompound tagCompound = readNbt(byteBuf);
return new ItemStack(item, amount, durability, tagCompound);
}
public static void writeSlot(ByteBuf byteBuf, ItemStack itemStack) {
if (itemStack == null || itemStack.getId() == 0)
byteBuf.writeShort(-1);
byteBuf.writeShort(itemStack.getId());
byteBuf.writeByte(itemStack.getAmount());
byteBuf.writeShort(itemStack.getData());
writeNbt(byteBuf, itemStack.getTagCompound());
}
public static NbtTagCompound readNbt(ByteBuf byteBuf) {
int readerIndex = byteBuf.readerIndex();
byte type = byteBuf.readByte();
if (type == 0)
return null;
byteBuf.readerIndex(readerIndex);
return NbtCompressedStreamTools.read(new ByteBufInputStream(byteBuf), new NbtReadLimiter(0x20000));
}
public static void writeNbt(ByteBuf byteBuf, NbtTagCompound tagCompound) {
if (tagCompound == null || tagCompound.getType() == NbtTagType.TAG_END) {
byteBuf.writeByte(0);
return;
}
try {
NbtCompressedStreamTools.write(tagCompound, new ByteBufOutputStream(byteBuf));
} catch (IOException e) {
throw new RuntimeException("Something went wrong while writing an NbtTagCompound!", e);
}
}
public static List<Metadata> readMetadata(ByteBuf byteBuf) {
List<Metadata> metadataList = new ArrayList<>();
byte item;
while ((item = byteBuf.readByte()) != 0x7f) {
MetadataType type = MetadataType.getById((item & 0xe0) >> 5);
int index = item & 0x1f;
switch (type) {
case BYTE: {
metadataList.add(new Metadata<>(index, byteBuf.readByte()));
break;
}
case SHORT: {
metadataList.add(new Metadata<>(index, byteBuf.readShort()));
break;
}
case INT: {
metadataList.add(new Metadata<>(index, byteBuf.readInt()));
break;
}
case FLOAT: {
metadataList.add(new Metadata<>(index, byteBuf.readFloat()));
break;
}
case STRING: {
metadataList.add(new Metadata<>(index, readUTF(byteBuf)));
break;
}
case ITEM: {
metadataList.add(new Metadata<>(index, readSlot(byteBuf)));
break;
}
case POSITION: {
int x = byteBuf.readInt();
int y = byteBuf.readInt();
int z = byteBuf.readInt();
metadataList.add(new Metadata<>(index, new Position(x, y, z)));
break;
}
case ROTATION: {
float pitch = byteBuf.readFloat();
float yaw = byteBuf.readFloat();
float roll = byteBuf.readFloat();
metadataList.add(new Metadata<>(index, new Rotation(pitch, yaw, roll)));
break;
}
}
}
return metadataList;
}
public static void writeMetadata(ByteBuf byteBuf, List<Metadata> metadata) {
for (Metadata entry : metadata) {
int index = entry.getIndex();
Object value = entry.getValue();
if (value == null)
continue;
int type = entry.getType().getId();
byteBuf.writeByte((type << 5) | index);
switch (entry.getType()) {
case BYTE: {
byteBuf.writeByte((byte) entry.getValue());
break;
}
case SHORT: {
byteBuf.writeShort((short) entry.getValue());
break;
}
case INT: {
byteBuf.writeInt((int) entry.getValue());
break;
}
case FLOAT: {
byteBuf.writeFloat((float) entry.getValue());
break;
}
case STRING: {
writeUTF(byteBuf, (String) entry.getValue());
break;
}
case ITEM: {
writeSlot(byteBuf, (ItemStack) entry.getValue());
break;
}
case POSITION: {
Position position = (Position) entry.getValue();
byteBuf.writeInt(position.getX());
byteBuf.writeInt(position.getY());
byteBuf.writeInt(position.getZ());
break;
}
case ROTATION: {
Rotation rotation = (Rotation) entry.getValue();
byteBuf.writeFloat(rotation.getPitch());
byteBuf.writeFloat(rotation.getYaw());
byteBuf.writeFloat(rotation.getRoll());
break;
}
}
}
byteBuf.writeByte(0x7f);
}
public static int readVarInt(ByteBuf byteBuf) {
int out = 0;
int bytes = 0;
byte in;
while (true) {
in = byteBuf.readByte();
out |= (in & 0x7F) << (bytes++ * 7);
if (bytes > 5) {
throw new IllegalStateException("Integer is bigger than maximum allowed!");
}
if ((in & 0x80) != 0x80) {
break;
}
}
return out;
}
public static void writeVarInt(ByteBuf byteBuf, int varInt) {
int part;
while (true) {
part = varInt & 0x7F;
varInt >>>= 7;
if (varInt != 0) {
part |= 0x80;
}
byteBuf.writeByte(part);
if (varInt == 0) {
break;
}
}
}
public static long readVarLong(ByteBuf buf) {
long out = 0;
int bytes = 0;
byte in;
while (true) {
in = buf.readByte();
out |= (in & 0x7F) << (bytes++ * 7);
if (bytes > 10) {
throw new IllegalStateException("Attempt to read long bigger than allowed for a varlong!");
}
if ((in & 0x80) != 0x80) {
break;
}
}
return out;
}
public static void writeVarLong(ByteBuf buf, long value) {
byte part;
while (true) {
part = (byte) (value & 0x7F);
value >>>= 7;
if (value != 0) {
part |= 0x80;
}
buf.writeByte(part);
if (value == 0) {
break;
}
}
}
public static String readUTF(ByteBuf byteBuf) {
byte[] bytes = new byte[readVarInt(byteBuf)];
byteBuf.readBytes(bytes);
return new String(bytes, StandardCharsets.UTF_8);
}
public static void writeUTF(ByteBuf byteBuf, String utf) {
byte[] data = utf.getBytes(StandardCharsets.UTF_8);
writeVarInt(byteBuf, utf.length());
byteBuf.writeBytes(data);
}
public static String[] readStringArray(ByteBuf byteBuf){
int count = readVarInt(byteBuf);
String[] entries = new String[count];
for (int i = 0; i < count; i++){
entries[i] = ByteBufUtils.readUTF(byteBuf);
}
return entries;
}
public static void writeStringArray(ByteBuf byteBuf, String[] array){
ByteBufUtils.writeVarInt(byteBuf, array.length);
for (String string : array){
ByteBufUtils.writeUTF(byteBuf, string);
}
}
}