2020-04-24 03:25:58 +02:00
|
|
|
package net.minestom.server.utils;
|
2019-08-03 15:25:24 +02:00
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
import io.netty.buffer.ByteBuf;
|
2020-06-22 23:25:00 +02:00
|
|
|
import net.minestom.server.chat.ColoredText;
|
2020-04-26 19:17:04 +02:00
|
|
|
import net.minestom.server.instance.Chunk;
|
2020-04-29 20:17:04 +02:00
|
|
|
import net.minestom.server.item.Enchantment;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.item.ItemStack;
|
2020-05-28 23:43:12 +02:00
|
|
|
import net.minestom.server.item.attribute.ItemAttribute;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.network.packet.PacketReader;
|
|
|
|
import net.minestom.server.network.packet.PacketWriter;
|
2020-05-27 12:33:12 +02:00
|
|
|
import net.minestom.server.potion.PotionType;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.utils.buffer.BufferWrapper;
|
2020-05-27 12:33:12 +02:00
|
|
|
import net.minestom.server.utils.item.NbtReaderUtils;
|
2020-05-30 00:01:38 +02:00
|
|
|
import net.minestom.server.utils.nbt.NBT;
|
|
|
|
import net.minestom.server.utils.nbt.NbtWriter;
|
2019-08-03 15:25:24 +02:00
|
|
|
|
2020-05-28 23:43:12 +02:00
|
|
|
import java.util.*;
|
2019-08-03 15:25:24 +02:00
|
|
|
|
|
|
|
public class Utils {
|
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
public static void writeVarIntBuf(ByteBuf buffer, int value) {
|
|
|
|
do {
|
|
|
|
byte temp = (byte) (value & 0b01111111);
|
|
|
|
value >>>= 7;
|
|
|
|
if (value != 0) {
|
|
|
|
temp |= 0b10000000;
|
|
|
|
}
|
|
|
|
buffer.writeByte(temp);
|
|
|
|
} while (value != 0);
|
2020-02-13 15:14:41 +01:00
|
|
|
}
|
|
|
|
|
2019-09-06 16:05:36 +02:00
|
|
|
public static void writeVarIntBuffer(BufferWrapper buffer, int value) {
|
2019-08-03 15:25:24 +02:00
|
|
|
do {
|
|
|
|
byte temp = (byte) (value & 0b01111111);
|
|
|
|
value >>>= 7;
|
|
|
|
if (value != 0) {
|
|
|
|
temp |= 0b10000000;
|
|
|
|
}
|
|
|
|
buffer.putByte(temp);
|
|
|
|
} while (value != 0);
|
|
|
|
}
|
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
public static void writeVarInt(PacketWriter writer, int value) {
|
2019-09-02 06:02:12 +02:00
|
|
|
do {
|
|
|
|
byte temp = (byte) (value & 0b01111111);
|
|
|
|
value >>>= 7;
|
|
|
|
if (value != 0) {
|
|
|
|
temp |= 0b10000000;
|
|
|
|
}
|
2020-04-17 01:16:02 +02:00
|
|
|
writer.writeByte(temp);
|
2019-09-02 06:02:12 +02:00
|
|
|
} while (value != 0);
|
|
|
|
}
|
|
|
|
|
2019-08-03 15:25:24 +02:00
|
|
|
public static int lengthVarInt(int value) {
|
|
|
|
int i = 0;
|
|
|
|
do {
|
|
|
|
i++;
|
|
|
|
byte temp = (byte) (value & 0b01111111);
|
|
|
|
value >>>= 7;
|
|
|
|
if (value != 0) {
|
|
|
|
temp |= 0b10000000;
|
|
|
|
}
|
2019-08-07 03:42:48 +02:00
|
|
|
} while (value != 0);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
public static int readVarInt(ByteBuf buffer) {
|
|
|
|
int numRead = 0;
|
|
|
|
int result = 0;
|
|
|
|
byte read;
|
|
|
|
do {
|
|
|
|
read = buffer.readByte();
|
|
|
|
int value = (read & 0b01111111);
|
|
|
|
result |= (value << (7 * numRead));
|
|
|
|
|
|
|
|
numRead++;
|
|
|
|
if (numRead > 5) {
|
|
|
|
throw new RuntimeException("VarInt is too big");
|
|
|
|
}
|
|
|
|
} while ((read & 0b10000000) != 0);
|
|
|
|
|
|
|
|
return result;
|
2019-08-03 15:25:24 +02:00
|
|
|
}
|
|
|
|
|
2020-05-26 19:22:47 +02:00
|
|
|
public static void writeVarLong(PacketWriter writer, long value) {
|
|
|
|
do {
|
|
|
|
byte temp = (byte) (value & 0b01111111);
|
|
|
|
value >>>= 7;
|
|
|
|
if (value != 0) {
|
|
|
|
temp |= 0b10000000;
|
|
|
|
}
|
|
|
|
writer.writeByte(temp);
|
|
|
|
} while (value != 0);
|
|
|
|
}
|
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
public static void writeItemStack(PacketWriter packet, ItemStack itemStack) {
|
2020-02-13 15:14:41 +01:00
|
|
|
if (itemStack == null || itemStack.isAir()) {
|
2020-04-17 01:16:02 +02:00
|
|
|
packet.writeBoolean(false);
|
2019-08-13 17:52:09 +02:00
|
|
|
} else {
|
2020-04-17 01:16:02 +02:00
|
|
|
packet.writeBoolean(true);
|
2020-05-22 21:46:50 +02:00
|
|
|
packet.writeVarInt(itemStack.getMaterialId());
|
2020-04-17 01:16:02 +02:00
|
|
|
packet.writeByte(itemStack.getAmount());
|
2019-08-22 14:52:32 +02:00
|
|
|
|
2020-02-13 15:14:41 +01:00
|
|
|
if (!itemStack.hasNbtTag()) {
|
2020-04-17 01:16:02 +02:00
|
|
|
packet.writeByte((byte) 0x00); // No nbt
|
2020-02-13 15:14:41 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
NbtWriter mainWriter = new NbtWriter(packet);
|
2019-08-22 14:52:32 +02:00
|
|
|
|
2020-06-01 18:57:16 +02:00
|
|
|
mainWriter.writeCompound("", writer -> {
|
2020-05-30 00:01:38 +02:00
|
|
|
// Unbreakable
|
|
|
|
if (itemStack.isUnbreakable()) {
|
|
|
|
writer.writeInt("Unbreakable", 1);
|
2020-02-13 15:14:41 +01:00
|
|
|
}
|
2019-08-22 14:52:32 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
// Start damage
|
|
|
|
{
|
|
|
|
writer.writeShort("Damage", itemStack.getDamage());
|
2020-02-13 15:14:41 +01:00
|
|
|
}
|
2020-05-30 00:01:38 +02:00
|
|
|
// End damage
|
|
|
|
|
|
|
|
// Display
|
|
|
|
boolean hasDisplayName = itemStack.hasDisplayName();
|
|
|
|
boolean hasLore = itemStack.hasLore();
|
|
|
|
|
|
|
|
if (hasDisplayName || hasLore) {
|
|
|
|
writer.writeCompound("display", displayWriter -> {
|
|
|
|
if (hasDisplayName) {
|
2020-06-22 23:25:00 +02:00
|
|
|
final String name = itemStack.getDisplayName().toString();
|
2020-05-30 00:01:38 +02:00
|
|
|
displayWriter.writeString("Name", name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hasLore) {
|
2020-06-22 23:25:00 +02:00
|
|
|
final ArrayList<ColoredText> lore = itemStack.getLore();
|
2020-05-30 00:01:38 +02:00
|
|
|
|
|
|
|
displayWriter.writeList("Lore", NBT.NBT_STRING, lore.size(), () -> {
|
2020-06-22 23:25:00 +02:00
|
|
|
for (ColoredText line : lore) {
|
|
|
|
packet.writeShortSizedString(line.toString());
|
2020-05-30 00:01:38 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
2020-05-29 01:05:08 +02:00
|
|
|
}
|
2020-05-30 00:01:38 +02:00
|
|
|
// End display
|
|
|
|
|
|
|
|
// Start enchantment
|
|
|
|
{
|
|
|
|
Map<Enchantment, Short> enchantmentMap = itemStack.getEnchantmentMap();
|
|
|
|
if (!enchantmentMap.isEmpty()) {
|
|
|
|
writeEnchant(writer, "Enchantments", enchantmentMap);
|
|
|
|
}
|
2020-04-29 20:17:04 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
Map<Enchantment, Short> storedEnchantmentMap = itemStack.getStoredEnchantmentMap();
|
|
|
|
if (!storedEnchantmentMap.isEmpty()) {
|
|
|
|
writeEnchant(writer, "StoredEnchantments", storedEnchantmentMap);
|
|
|
|
}
|
2020-04-29 20:17:04 +02:00
|
|
|
}
|
2020-05-30 00:01:38 +02:00
|
|
|
// End enchantment
|
2020-04-29 20:17:04 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
// Start attribute
|
|
|
|
{
|
|
|
|
List<ItemAttribute> itemAttributes = itemStack.getAttributes();
|
|
|
|
if (!itemAttributes.isEmpty()) {
|
|
|
|
packet.writeByte((byte) 0x09); // Type id (list)
|
|
|
|
packet.writeShortSizedString("AttributeModifiers");
|
2020-05-28 23:43:12 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
packet.writeByte((byte) 0x0A); // Compound
|
|
|
|
packet.writeInt(itemAttributes.size());
|
2020-05-28 23:43:12 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
for (ItemAttribute itemAttribute : itemAttributes) {
|
|
|
|
UUID uuid = itemAttribute.getUuid();
|
2020-05-28 23:43:12 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
writer.writeLong("UUIDMost", uuid.getMostSignificantBits());
|
2020-05-28 23:43:12 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
writer.writeLong("UUIDLeast", uuid.getLeastSignificantBits());
|
2020-05-28 23:43:12 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
writer.writeDouble("Amount", itemAttribute.getValue());
|
2020-05-28 23:43:12 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
writer.writeString("Slot", itemAttribute.getSlot().name().toLowerCase());
|
2020-05-28 23:43:12 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
writer.writeString("itemAttribute", itemAttribute.getAttribute().getKey());
|
2020-05-28 23:43:12 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
writer.writeInt("Operation", itemAttribute.getOperation().getId());
|
2020-05-28 23:43:12 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
writer.writeString("Name", itemAttribute.getInternalName());
|
|
|
|
}
|
|
|
|
packet.writeByte((byte) 0x00); // End compound
|
2020-05-28 23:43:12 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-30 00:01:38 +02:00
|
|
|
// End attribute
|
|
|
|
|
|
|
|
// Start potion
|
|
|
|
{
|
|
|
|
Set<PotionType> potionTypes = itemStack.getPotionTypes();
|
|
|
|
if (!potionTypes.isEmpty()) {
|
|
|
|
for (PotionType potionType : potionTypes) {
|
|
|
|
packet.writeByte((byte) 0x08); // type id (string)
|
|
|
|
packet.writeShortSizedString("Potion");
|
|
|
|
packet.writeShortSizedString("minecraft:" + potionType.name().toLowerCase());
|
|
|
|
}
|
2020-05-27 12:33:12 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-30 00:01:38 +02:00
|
|
|
// End potion
|
2020-05-22 21:46:50 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
// Start hide flags
|
|
|
|
{
|
|
|
|
int hideFlag = itemStack.getHideFlag();
|
|
|
|
if (hideFlag != 0) {
|
|
|
|
writer.writeInt("HideFlags", hideFlag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
private static void writeEnchant(NbtWriter writer, String listName, Map<Enchantment, Short> enchantmentMap) {
|
|
|
|
writer.writeList(listName, NBT.NBT_COMPOUND, enchantmentMap.size(), () -> {
|
|
|
|
for (Map.Entry<Enchantment, Short> entry : enchantmentMap.entrySet()) {
|
|
|
|
Enchantment enchantment = entry.getKey();
|
|
|
|
short level = entry.getValue();
|
2020-05-29 01:05:08 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
writer.writeShort("lvl", level);
|
2020-05-29 01:05:08 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
writer.writeString("id", "minecraft:" + enchantment.name().toLowerCase());
|
2020-05-29 01:05:08 +02:00
|
|
|
|
2020-05-30 00:01:38 +02:00
|
|
|
}
|
|
|
|
});
|
2020-05-29 01:05:08 +02:00
|
|
|
}
|
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
public static ItemStack readItemStack(PacketReader reader) {
|
|
|
|
boolean present = reader.readBoolean();
|
2020-02-16 19:11:36 +01:00
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
if (!present) {
|
2020-04-23 12:30:49 +02:00
|
|
|
return ItemStack.getAirItem();
|
2020-04-17 01:16:02 +02:00
|
|
|
}
|
2020-02-16 19:11:36 +01:00
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
int id = reader.readVarInt();
|
|
|
|
if (id == -1) {
|
|
|
|
// Drop mode
|
2020-04-23 12:30:49 +02:00
|
|
|
return ItemStack.getAirItem();
|
2020-04-17 01:16:02 +02:00
|
|
|
}
|
2020-02-13 15:14:41 +01:00
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
byte count = reader.readByte();
|
2020-02-09 15:34:09 +01:00
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
ItemStack item = new ItemStack((short) id, count);
|
2020-02-09 15:34:09 +01:00
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
byte nbt = reader.readByte(); // Should be compound start (0x0A) or 0 if there isn't NBT data
|
2020-02-09 15:34:09 +01:00
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
if (nbt == 0x00) {
|
|
|
|
return item;
|
|
|
|
} else if (nbt == 0x0A) {
|
2020-05-22 18:51:03 +02:00
|
|
|
reader.readShort(); // Ignored, should be empty (main compound name)
|
2020-04-17 01:16:02 +02:00
|
|
|
NbtReaderUtils.readItemStackNBT(reader, item);
|
|
|
|
}
|
2020-02-09 15:34:09 +01:00
|
|
|
|
2020-04-17 01:16:02 +02:00
|
|
|
return item;
|
2020-02-09 15:34:09 +01:00
|
|
|
}
|
|
|
|
|
2019-09-06 16:05:36 +02:00
|
|
|
public static void writeBlocks(BufferWrapper buffer, short[] blocksId, int bitsPerEntry) {
|
2019-08-21 16:50:52 +02:00
|
|
|
short count = 0;
|
|
|
|
for (short id : blocksId)
|
|
|
|
if (id != 0)
|
|
|
|
count++;
|
|
|
|
|
|
|
|
buffer.putShort(count);
|
2019-08-10 04:16:01 +02:00
|
|
|
buffer.putByte((byte) bitsPerEntry);
|
2020-04-26 19:17:04 +02:00
|
|
|
int[] blocksData = new int[Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SIZE_Z];
|
|
|
|
for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
|
|
|
|
for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
|
|
|
|
for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
|
2019-08-10 04:16:01 +02:00
|
|
|
int sectionIndex = (((y * 16) + x) * 16) + z;
|
|
|
|
int index = y << 8 | z << 4 | x;
|
2019-08-18 20:38:09 +02:00
|
|
|
blocksData[index] = blocksId[sectionIndex];
|
2019-08-10 04:16:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-06 16:05:36 +02:00
|
|
|
long[] data = encodeBlocks(blocksData, bitsPerEntry);
|
|
|
|
buffer.putVarInt(data.length);
|
2019-08-10 04:16:01 +02:00
|
|
|
for (int i = 0; i < data.length; i++) {
|
|
|
|
buffer.putLong(data[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static long[] encodeBlocks(int[] blocks, int bitsPerEntry) {
|
|
|
|
long maxEntryValue = (1L << bitsPerEntry) - 1;
|
|
|
|
|
|
|
|
int length = (int) Math.ceil(blocks.length * bitsPerEntry / 64.0);
|
|
|
|
long[] data = new long[length];
|
|
|
|
|
|
|
|
for (int index = 0; index < blocks.length; index++) {
|
|
|
|
int value = blocks[index];
|
|
|
|
int bitIndex = index * bitsPerEntry;
|
|
|
|
int startIndex = bitIndex / 64;
|
|
|
|
int endIndex = ((index + 1) * bitsPerEntry - 1) / 64;
|
|
|
|
int startBitSubIndex = bitIndex % 64;
|
|
|
|
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
|
|
|
if (startIndex != endIndex) {
|
|
|
|
int endBitSubIndex = 64 - startBitSubIndex;
|
|
|
|
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-08-03 15:25:24 +02:00
|
|
|
}
|