mirror of https://github.com/Minestom/Minestom.git
Optimization & proper ItemStack reader
This commit is contained in:
parent
e9809b20ac
commit
b8319217ab
|
@ -535,6 +535,21 @@ public abstract class Entity implements Viewable, DataContainer {
|
|||
return position;
|
||||
}
|
||||
|
||||
public boolean sameChunk(Position position) {
|
||||
Position pos = getPosition();
|
||||
int chunkX1 = ChunkUtils.getChunkX((int) pos.getX());
|
||||
int chunkZ1 = ChunkUtils.getChunkX((int) pos.getZ());
|
||||
|
||||
int chunkX2 = ChunkUtils.getChunkX((int) position.getX());
|
||||
int chunkZ2 = ChunkUtils.getChunkX((int) position.getZ());
|
||||
|
||||
return chunkX1 == chunkX2 && chunkZ1 == chunkZ2;
|
||||
}
|
||||
|
||||
public boolean sameChunk(Entity entity) {
|
||||
return sameChunk(entity.getPosition());
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
this.shouldRemove = true;
|
||||
entityById.remove(id);
|
||||
|
|
|
@ -15,6 +15,9 @@ public class EntityManager {
|
|||
|
||||
private static InstanceManager instanceManager = Main.getInstanceManager();
|
||||
|
||||
private UpdateType updateType = UpdateType.PER_CHUNK;
|
||||
private Set<Instance> instances = instanceManager.getInstances();
|
||||
|
||||
private ExecutorService entitiesPool = new MinestomThread(Main.THREAD_COUNT_ENTITIES, "Ms-EntitiesPool");
|
||||
private ExecutorService playersPool = new MinestomThread(Main.THREAD_COUNT_PLAYERS_ENTITIES, "Ms-PlayersPool");
|
||||
|
||||
|
@ -23,10 +26,45 @@ public class EntityManager {
|
|||
public void update() {
|
||||
final long time = System.currentTimeMillis();
|
||||
|
||||
// Connect waiting players
|
||||
waitingPlayersTick();
|
||||
for (Instance instance : instanceManager.getInstances()) {
|
||||
testTick2(instance, time);
|
||||
|
||||
// Update entities
|
||||
switch (updateType) {
|
||||
case PER_CHUNK:
|
||||
chunkUpdate(instances, time);
|
||||
break;
|
||||
case PER_ENTITY_TYPE:
|
||||
entityTypeUpdate(instances, time);
|
||||
break;
|
||||
case PER_INSTANCE:
|
||||
instanceUpdate(instances, time);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update is chunk based
|
||||
*
|
||||
* @param time
|
||||
*/
|
||||
private void chunkUpdate(Set<Instance> instances, long time) {
|
||||
// TODO optimize for when there are too many entities on one chunk
|
||||
for (Instance instance : instanceManager.getInstances()) {
|
||||
for (Chunk chunk : instance.getChunks()) {
|
||||
Set<Entity> entities = instance.getChunkEntities(chunk);
|
||||
|
||||
if (!entities.isEmpty()) {
|
||||
entitiesPool.execute(() -> {
|
||||
for (Entity entity : entities) {
|
||||
entity.tick(time);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void waitingPlayersTick() {
|
||||
|
@ -43,48 +81,81 @@ public class EntityManager {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO optimize for when there are too many entities on one chunk
|
||||
private void testTick2(Instance instance, long time) {
|
||||
for (Chunk chunk : instance.getChunks()) {
|
||||
Set<Entity> entities = instance.getChunkEntities(chunk);
|
||||
/**
|
||||
* Update each entity type separately independently of their location
|
||||
*
|
||||
* @param time
|
||||
*/
|
||||
private void entityTypeUpdate(Set<Instance> instances, long time) {
|
||||
for (Instance instance : instanceManager.getInstances()) {
|
||||
Set<Player> players = instance.getPlayers();
|
||||
Set<EntityCreature> creatures = instance.getCreatures();
|
||||
Set<ObjectEntity> objects = instance.getObjectEntities();
|
||||
|
||||
if (!entities.isEmpty()) {
|
||||
if (!players.isEmpty()) {
|
||||
playersPool.execute(() -> {
|
||||
for (Player player : players) {
|
||||
player.tick(time);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!creatures.isEmpty() || !objects.isEmpty()) {
|
||||
entitiesPool.execute(() -> {
|
||||
for (Entity entity : entities) {
|
||||
entity.tick(time);
|
||||
for (EntityCreature creature : creatures) {
|
||||
creature.tick(time);
|
||||
}
|
||||
for (ObjectEntity objectEntity : objects) {
|
||||
objectEntity.tick(time);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void testTick1(Instance instance, long time) {
|
||||
Set<ObjectEntity> objects = instance.getObjectEntities();
|
||||
Set<EntityCreature> creatures = instance.getCreatures();
|
||||
Set<Player> players = instance.getPlayers();
|
||||
/**
|
||||
* Each instance get its pool, should suppress most of the problems related to thread-safety
|
||||
*
|
||||
* @param instances
|
||||
* @param time
|
||||
*/
|
||||
private void instanceUpdate(Set<Instance> instances, long time) {
|
||||
for (Instance instance : instances) {
|
||||
Set<Player> players = instance.getPlayers();
|
||||
Set<EntityCreature> creatures = instance.getCreatures();
|
||||
Set<ObjectEntity> objects = instance.getObjectEntities();
|
||||
|
||||
if (!creatures.isEmpty() || !objects.isEmpty()) {
|
||||
entitiesPool.execute(() -> {
|
||||
for (EntityCreature creature : creatures) {
|
||||
creature.tick(time);
|
||||
}
|
||||
for (ObjectEntity objectEntity : objects) {
|
||||
objectEntity.tick(time);
|
||||
}
|
||||
});
|
||||
if (!players.isEmpty() || !creatures.isEmpty() || !objects.isEmpty()) {
|
||||
entitiesPool.execute(() -> {
|
||||
for (Player player : players) {
|
||||
player.tick(time);
|
||||
}
|
||||
for (EntityCreature creature : creatures) {
|
||||
creature.tick(time);
|
||||
}
|
||||
for (ObjectEntity objectEntity : objects) {
|
||||
objectEntity.tick(time);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!players.isEmpty()) {
|
||||
playersPool.execute(() -> {
|
||||
for (Player player : players) {
|
||||
player.tick(time);
|
||||
}
|
||||
});
|
||||
}
|
||||
public UpdateType getUpdateType() {
|
||||
return updateType;
|
||||
}
|
||||
|
||||
public void addWaitingPlayer(Player player) {
|
||||
this.waitingPlayers.add(player);
|
||||
}
|
||||
|
||||
public void setUpdateType(UpdateType updateType) {
|
||||
this.updateType = updateType;
|
||||
}
|
||||
|
||||
public enum UpdateType {
|
||||
PER_CHUNK,
|
||||
PER_ENTITY_TYPE,
|
||||
PER_INSTANCE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -181,7 +181,12 @@ public class Player extends LivingEntity {
|
|||
//itemEntity.remove();
|
||||
}*/
|
||||
|
||||
getInventory().addItemStack(new ItemStack(1, (byte) 75));
|
||||
ItemStack item = new ItemStack(1, (byte) 75);
|
||||
item.setDisplayName("LE NOM PUTAIN");
|
||||
item.getLore().add("lol le lore");
|
||||
item.getLore().add("lol le lore2");
|
||||
item.getLore().add("lol le lore3");
|
||||
getInventory().addItemStack(item);
|
||||
//getInventory().addItemStack(new ItemStack(1, (byte) 100));
|
||||
|
||||
/*TeamManager teamManager = Main.getTeamManager();
|
||||
|
|
|
@ -159,8 +159,8 @@ public abstract class Instance implements BlockModifier, DataContainer {
|
|||
}
|
||||
|
||||
public Chunk getChunkAt(double x, double z) {
|
||||
int chunkX = Math.floorDiv((int) x, 16);
|
||||
int chunkZ = Math.floorDiv((int) z, 16);
|
||||
int chunkX = ChunkUtils.getChunkX((int) x);
|
||||
int chunkZ = ChunkUtils.getChunkX((int) z);
|
||||
return getChunk(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package fr.themode.minestom.item;
|
|||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.data.DataContainer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ItemStack implements DataContainer {
|
||||
|
||||
public static final ItemStack AIR_ITEM = new ItemStack(0, (byte) 1);
|
||||
|
@ -13,6 +15,7 @@ public class ItemStack implements DataContainer {
|
|||
|
||||
private String displayName;
|
||||
private boolean unbreakable;
|
||||
private ArrayList<String> lore;
|
||||
|
||||
private Data data;
|
||||
|
||||
|
@ -20,6 +23,7 @@ public class ItemStack implements DataContainer {
|
|||
this.material = material;
|
||||
this.amount = amount;
|
||||
this.damage = damage;
|
||||
this.lore = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ItemStack(int id, byte amount) {
|
||||
|
@ -30,11 +34,18 @@ public class ItemStack implements DataContainer {
|
|||
return material == Material.AIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not take amount in consideration
|
||||
*
|
||||
* @param itemStack
|
||||
* @return
|
||||
*/
|
||||
public boolean isSimilar(ItemStack itemStack) {
|
||||
return itemStack.getMaterial() == material &&
|
||||
itemStack.getDisplayName() == displayName &&
|
||||
itemStack.isUnbreakable() == unbreakable &&
|
||||
itemStack.getDamage() == damage;
|
||||
itemStack.getDamage() == damage &&
|
||||
itemStack.getData() == data;
|
||||
}
|
||||
|
||||
public byte getAmount() {
|
||||
|
@ -65,6 +76,22 @@ public class ItemStack implements DataContainer {
|
|||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public boolean hasDisplayName() {
|
||||
return displayName != null;
|
||||
}
|
||||
|
||||
public ArrayList<String> getLore() {
|
||||
return lore;
|
||||
}
|
||||
|
||||
public void setLore(ArrayList<String> lore) {
|
||||
this.lore = lore;
|
||||
}
|
||||
|
||||
public boolean hasLore() {
|
||||
return lore != null && !lore.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isUnbreakable() {
|
||||
return unbreakable;
|
||||
}
|
||||
|
@ -73,6 +100,10 @@ public class ItemStack implements DataContainer {
|
|||
this.unbreakable = unbreakable;
|
||||
}
|
||||
|
||||
public boolean hasNbtTag() {
|
||||
return hasDisplayName() || hasLore() || isUnbreakable();
|
||||
}
|
||||
|
||||
public ItemStack clone() {
|
||||
ItemStack itemStack = new ItemStack(material, amount, damage);
|
||||
itemStack.setDisplayName(displayName);
|
||||
|
|
|
@ -50,6 +50,11 @@ public class PacketReader {
|
|||
client.readShort(consumer);
|
||||
}
|
||||
|
||||
public void readInteger(IntConsumer consumer) {
|
||||
sizeOffset += Integer.BYTES;
|
||||
client.readInt(consumer);
|
||||
}
|
||||
|
||||
public void readLong(LongConsumer consumer) {
|
||||
sizeOffset += Long.BYTES;
|
||||
client.readLong(consumer);
|
||||
|
@ -66,13 +71,17 @@ public class PacketReader {
|
|||
}
|
||||
|
||||
public void readSizedString(StringConsumer consumer) {
|
||||
Utils.readString(client, consumer);
|
||||
Utils.readStringVarIntSized(client, consumer);
|
||||
}
|
||||
|
||||
public void readSizedString(Consumer<String> consumer) {
|
||||
readSizedString((string, length1) -> consumer.accept(string));
|
||||
}
|
||||
|
||||
public void readShortSizedString(StringConsumer consumer) {
|
||||
Utils.readStringShortSized(client, consumer);
|
||||
}
|
||||
|
||||
public void getRemainingBytes(int offset, Consumer<byte[]> consumer) {
|
||||
int size = length - 1 - offset;
|
||||
client.readBytes(size, consumer);
|
||||
|
@ -90,4 +99,11 @@ public class PacketReader {
|
|||
Utils.readItemStack(this, consumer);
|
||||
}
|
||||
|
||||
public int getPacketLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public int getReaderOffset() {
|
||||
return sizeOffset;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ public class ClientPacketsHandler {
|
|||
}
|
||||
|
||||
public ClientPacket getPacketInstance(int id) {
|
||||
//System.out.println("RECEIVED PACKET 0x" + Integer.toHexString(id));
|
||||
if (id > SIZE)
|
||||
throw new IllegalStateException("Packet ID 0x" + Integer.toHexString(id) + " has been tried to be parsed, debug needed");
|
||||
|
||||
|
|
|
@ -5,28 +5,31 @@ import fr.themode.minestom.net.packet.client.play.*;
|
|||
public class ClientPlayPacketsHandler extends ClientPacketsHandler {
|
||||
|
||||
public ClientPlayPacketsHandler() {
|
||||
register(0x05, ClientSettingsPacket.class);
|
||||
register(0x0B, ClientPluginMessagePacket.class);
|
||||
register(0x11, ClientPlayerPositionPacket.class);
|
||||
register(0x12, ClientPlayerPositionAndLookPacket.class);
|
||||
register(0x00, ClientTeleportConfirmPacket.class);
|
||||
register(0x0F, ClientKeepAlivePacket.class);
|
||||
register(0x19, ClientPlayerAbilitiesPacket.class);
|
||||
register(0x13, ClientPlayerLookPacket.class);
|
||||
register(0x14, ClientPlayerPacket.class);
|
||||
register(0x2A, ClientAnimationPacket.class);
|
||||
register(0x1B, ClientEntityActionPacket.class);
|
||||
register(0x0E, ClientUseEntityPacket.class);
|
||||
register(0x03, ClientChatMessagePacket.class);
|
||||
register(0x1A, ClientPlayerDiggingPacket.class);
|
||||
register(0x2C, ClientPlayerBlockPlacementPacket.class);
|
||||
register(0x23, ClientHeldItemChangePacket.class);
|
||||
register(0x04, ClientStatusPacket.class);
|
||||
register(0x05, ClientSettingsPacket.class);
|
||||
register(0x07, ClientWindowConfirmationPacket.class);
|
||||
register(0x08, ClientClickWindowButtonPacket.class); // Marked as 0x07 on wiki.vg
|
||||
register(0x09, ClientClickWindowPacket.class);
|
||||
register(0x0A, ClientCloseWindow.class);
|
||||
register(0x07, ClientClickWindowButtonPacket.class);
|
||||
register(0x0B, ClientPluginMessagePacket.class);
|
||||
register(0x1C, ClientSteerVehiclePacket.class);
|
||||
register(0x2D, ClientUseItemPacket.class);
|
||||
register(0x04, ClientStatusPacket.class);
|
||||
register(0x0E, ClientUseEntityPacket.class);
|
||||
register(0x0F, ClientKeepAlivePacket.class);
|
||||
|
||||
register(0x11, ClientPlayerPositionPacket.class);
|
||||
register(0x12, ClientPlayerPositionAndLookPacket.class);
|
||||
register(0x13, ClientPlayerLookPacket.class);
|
||||
register(0x14, ClientPlayerPacket.class);
|
||||
register(0x19, ClientPlayerAbilitiesPacket.class);
|
||||
register(0x1A, ClientPlayerDiggingPacket.class);
|
||||
register(0x1B, ClientEntityActionPacket.class);
|
||||
|
||||
register(0x23, ClientHeldItemChangePacket.class);
|
||||
register(0x26, ClientCreativeInventoryActionPacket.class);
|
||||
register(0x2A, ClientAnimationPacket.class);
|
||||
register(0x2C, ClientPlayerBlockPlacementPacket.class);
|
||||
register(0x2D, ClientUseItemPacket.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ public class ClientClickWindowButtonPacket extends ClientPlayPacket {
|
|||
|
||||
@Override
|
||||
public void read(PacketReader reader, Runnable callback) {
|
||||
// FIXME: 2 packets have the same id (Confirm Transaction / Click window button)
|
||||
reader.readByte(value -> windowId = value);
|
||||
reader.readByte(value -> {
|
||||
buttonId = value;
|
||||
|
|
|
@ -12,7 +12,6 @@ public class ClientClickWindowPacket extends ClientPlayPacket {
|
|||
public short actionNumber;
|
||||
public int mode;
|
||||
public ItemStack item;
|
||||
// TODO clicked item
|
||||
|
||||
@Override
|
||||
public void read(PacketReader reader, Runnable callback) {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package fr.themode.minestom.net.packet.client.play;
|
||||
|
||||
import fr.themode.minestom.net.packet.PacketReader;
|
||||
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
|
||||
|
||||
public class ClientWindowConfirmationPacket extends ClientPlayPacket {
|
||||
|
||||
public byte windowId;
|
||||
public short actionNumber;
|
||||
public boolean accepted;
|
||||
|
||||
@Override
|
||||
public void read(PacketReader reader, Runnable callback) {
|
||||
reader.readByte(value -> windowId = value);
|
||||
reader.readShort(value -> actionNumber = value);
|
||||
reader.readBoolean(value -> {
|
||||
accepted = value;
|
||||
callback.run();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -8,6 +8,14 @@ public class ChunkUtils {
|
|||
return instance.getChunk((int) Math.floor(x / 16), (int) Math.floor(z / 16)) == null;
|
||||
}
|
||||
|
||||
public static int getChunkX(int x) {
|
||||
return Math.floorDiv(x, 16);
|
||||
}
|
||||
|
||||
public static int getChunkZ(int z) {
|
||||
return Math.floorDiv(z, 16);
|
||||
}
|
||||
|
||||
public static long getChunkIndex(int chunkX, int chunkZ) {
|
||||
return (((long) chunkX) << 32) | (chunkZ & 0xffffffffL);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
package fr.themode.minestom.utils;
|
||||
|
||||
import club.thectm.minecraft.text.LegacyText;
|
||||
import club.thectm.minecraft.text.TextObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import fr.themode.minestom.item.ItemStack;
|
||||
import fr.themode.minestom.net.packet.PacketReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class NbtReaderUtils {
|
||||
|
||||
public static void readItemStackNBT(PacketReader reader, ItemStack item) {
|
||||
reader.readByte(typeId -> {
|
||||
switch (typeId) {
|
||||
case 0x00: // TAG_End
|
||||
// End of item NBT
|
||||
break;
|
||||
case 0x01: // TAG_Byte
|
||||
|
||||
break;
|
||||
case 0x02: // TAG_Short
|
||||
reader.readShortSizedString((name, l) -> {
|
||||
|
||||
// Damage NBT
|
||||
if (name.equals("Damage")) {
|
||||
reader.readShort(damage -> {
|
||||
item.setDamage(damage);
|
||||
readItemStackNBT(reader, item);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
break;
|
||||
case 0x03: // TAG_Int
|
||||
reader.readShortSizedString((name, length) -> {
|
||||
|
||||
// Unbreakable
|
||||
if (name.equals("Unbreakable")) {
|
||||
reader.readInteger(value -> {
|
||||
item.setUnbreakable(value == 1);
|
||||
readItemStackNBT(reader, item);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
break;
|
||||
case 0x04: // TAG_Long
|
||||
|
||||
break;
|
||||
case 0x05: // TAG_Float
|
||||
|
||||
break;
|
||||
case 0x06: // TAG_Double
|
||||
|
||||
break;
|
||||
case 0x07: // TAG_Byte_Array
|
||||
|
||||
break;
|
||||
case 0x08: // TAG_String
|
||||
|
||||
break;
|
||||
case 0x09: // TAG_List
|
||||
|
||||
break;
|
||||
case 0x0A: // TAG_Compound
|
||||
|
||||
reader.readShortSizedString((compoundName, length) -> {
|
||||
|
||||
// Display Compound
|
||||
if (compoundName.equals("display")) {
|
||||
readItemStackDisplayNBT(reader, item);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void readItemStackDisplayNBT(PacketReader reader, ItemStack item) {
|
||||
|
||||
reader.readByte(typeId -> {
|
||||
switch (typeId) {
|
||||
case 0x00: // TAG_End
|
||||
// End of the display compound
|
||||
readItemStackNBT(reader, item);
|
||||
break;
|
||||
case 0x08: // TAG_String
|
||||
|
||||
reader.readShortSizedString((name, length) -> {
|
||||
|
||||
if (name.equals("Name")) {
|
||||
reader.readShortSizedString((jsonDisplayName, length1) -> {
|
||||
TextObject textObject = TextObject.fromJson(new JsonParser().parse(jsonDisplayName).getAsJsonObject());
|
||||
String displayName = LegacyText.toLegacy(textObject);
|
||||
item.setDisplayName(displayName);
|
||||
readItemStackDisplayNBT(reader, item);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
break;
|
||||
case 0x09: // TAG_List
|
||||
|
||||
reader.readShortSizedString((name, length) -> {
|
||||
|
||||
if (name.equals("Lore")) {
|
||||
reader.readByte(loreType -> { // Should always be 0x08 (TAG_String)
|
||||
|
||||
reader.readInteger(size -> {
|
||||
ArrayList<String> lore = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
reader.readShortSizedString((string, length1) -> {
|
||||
TextObject textObject = TextObject.fromJson(new JsonParser().parse(string).getAsJsonObject());
|
||||
String line = LegacyText.toLegacy(textObject);
|
||||
lore.add(line);
|
||||
});
|
||||
if (i == size - 1) { // Last iteration
|
||||
readItemStackDisplayNBT(reader, item);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,7 @@ public class PacketUtils {
|
|||
PacketWriter packetWriter = new PacketWriter(packet);
|
||||
serverPacket.write(packetWriter);
|
||||
|
||||
System.out.println("WRITE PACKET: " + id + " " + serverPacket.getClass().getSimpleName());
|
||||
//System.out.println("WRITE PACKET: " + id + " " + serverPacket.getClass().getSimpleName());
|
||||
|
||||
callback.accept(packet.prepend(p -> {
|
||||
Utils.writeVarInt(packet, packet.getSize());
|
||||
|
|
|
@ -10,6 +10,7 @@ import fr.themode.minestom.utils.buffer.BufferWrapper;
|
|||
import fr.themode.minestom.utils.consumer.StringConsumer;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Utils {
|
||||
|
@ -29,7 +30,7 @@ public class Utils {
|
|||
}
|
||||
}
|
||||
|
||||
public static void readString(Client client, StringConsumer consumer) {
|
||||
public static void readStringVarIntSized(Client client, StringConsumer consumer) {
|
||||
ConnectionUtils.readVarInt(client, length -> {
|
||||
int stringLength = Utils.lengthVarInt(length) + length;
|
||||
client.readBytes(length, bytes -> {
|
||||
|
@ -43,6 +44,20 @@ public class Utils {
|
|||
});
|
||||
}
|
||||
|
||||
public static void readStringShortSized(Client client, StringConsumer consumer) {
|
||||
|
||||
client.readShort(length -> {
|
||||
client.readBytes(length, bytes -> {
|
||||
try {
|
||||
consumer.accept(new String(bytes, "UTF-8"), length);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
consumer.accept(null, length);
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static void writeVarIntBuffer(BufferWrapper buffer, int value) {
|
||||
do {
|
||||
byte temp = (byte) (value & 0b01111111);
|
||||
|
@ -93,15 +108,20 @@ public class Utils {
|
|||
}
|
||||
|
||||
public static void writeItemStack(Packet packet, ItemStack itemStack) {
|
||||
if (itemStack == null) {
|
||||
if (itemStack == null || itemStack.isAir()) {
|
||||
packet.putBoolean(false);
|
||||
} else {
|
||||
packet.putBoolean(true);
|
||||
Utils.writeVarInt(packet, itemStack.getMaterial().getId());
|
||||
packet.putByte(itemStack.getAmount());
|
||||
|
||||
if (!itemStack.hasNbtTag()) {
|
||||
packet.putByte((byte) 0x00); // No nbt
|
||||
return;
|
||||
}
|
||||
|
||||
packet.putByte((byte) 0x0A); // Compound
|
||||
packet.putShort((short) 0);
|
||||
packet.putShort((short) 0); // Empty compound name
|
||||
|
||||
// Unbreakable
|
||||
if (itemStack.isUnbreakable()) {
|
||||
|
@ -116,29 +136,40 @@ public class Utils {
|
|||
packet.putShort(itemStack.getDamage());
|
||||
|
||||
// Display
|
||||
packet.putByte((byte) 0x0A); // Compound
|
||||
packet.putString("display");
|
||||
boolean hasDisplayName = itemStack.hasDisplayName();
|
||||
boolean hasLore = itemStack.hasLore();
|
||||
|
||||
if (itemStack.getDisplayName() != null) {
|
||||
packet.putByte((byte) 0x08);
|
||||
packet.putString("Name");
|
||||
packet.putString(Chat.legacyTextString(itemStack.getDisplayName()));
|
||||
if (hasDisplayName || hasLore) {
|
||||
packet.putByte((byte) 0x0A); // Start display compound
|
||||
packet.putString("display");
|
||||
|
||||
if (hasDisplayName) {
|
||||
packet.putByte((byte) 0x08);
|
||||
packet.putString("Name");
|
||||
packet.putString(Chat.legacyTextString(itemStack.getDisplayName()));
|
||||
}
|
||||
|
||||
if (hasLore) {
|
||||
ArrayList<String> lore = itemStack.getLore();
|
||||
|
||||
packet.putByte((byte) 0x09);
|
||||
packet.putString("Lore");
|
||||
packet.putByte((byte) 0x08);
|
||||
packet.putInt(lore.size());
|
||||
for (String line : lore) {
|
||||
packet.putString(Chat.legacyTextString(line));
|
||||
}
|
||||
}
|
||||
|
||||
packet.putByte((byte) 0); // End display compound
|
||||
}
|
||||
|
||||
// TODO lore
|
||||
/*packet.putByte((byte) 0x08);
|
||||
packet.putString("Lore");
|
||||
packet.putString(Chat.rawText("a line"));*/
|
||||
|
||||
packet.putByte((byte) 0); // End display compound
|
||||
|
||||
// End display
|
||||
|
||||
packet.putByte((byte) 0); // End nbt
|
||||
}
|
||||
}
|
||||
|
||||
public static void readItemStack(PacketReader reader, Consumer<ItemStack> consumer) {
|
||||
// FIXME: need finishing
|
||||
reader.readBoolean(present -> {
|
||||
if (!present) {
|
||||
consumer.accept(ItemStack.AIR_ITEM); // Consume air item if empty
|
||||
|
@ -146,11 +177,18 @@ public class Utils {
|
|||
}
|
||||
|
||||
reader.readVarInt(id -> {
|
||||
|
||||
reader.readByte(count -> {
|
||||
ItemStack item = new ItemStack(id, count);
|
||||
reader.readByte(nbt -> { // Should be compound start (0x0A) or 0 if there isn't NBT data
|
||||
if (nbt == 0x00) {
|
||||
consumer.accept(item);
|
||||
return;
|
||||
} else if (nbt == 0x0A) {
|
||||
reader.readShort(compoundName -> { // Ignored, should be empty (main compound name)
|
||||
NbtReaderUtils.readItemStackNBT(reader, item);
|
||||
});
|
||||
|
||||
reader.readByte(nbt -> { // FIXME: assume that there is no NBT data
|
||||
consumer.accept(new ItemStack(id, count));
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue