From c407e384abdacef9f87c7f70514044b5c19639c5 Mon Sep 17 00:00:00 2001 From: TheMode Date: Thu, 22 Aug 2019 14:52:32 +0200 Subject: [PATCH] Update --- src/main/java/fr/themode/minestom/Main.java | 8 +-- .../java/fr/themode/minestom/Viewable.java | 24 +++++++-- .../fr/themode/minestom/entity/Entity.java | 12 ++++- .../minestom/entity/EntityCreature.java | 6 +-- .../minestom/entity/EntityManager.java | 1 + .../themode/minestom/entity/LivingEntity.java | 28 ++++++++++ .../themode/minestom/entity/ObjectEntity.java | 10 ++-- .../fr/themode/minestom/entity/Player.java | 24 ++++++--- .../minestom/event/BlockPlaceEvent.java | 10 +++- .../themode/minestom/instance/BlockBatch.java | 8 +-- .../minestom/instance/BlockModifier.java | 11 ++++ .../fr/themode/minestom/instance/Chunk.java | 29 ++++++++-- .../themode/minestom/instance/Instance.java | 18 +++++-- .../instance/demo/ChunkGeneratorDemo.java | 2 +- .../minestom/instance/demo/StoneBlock.java | 2 +- .../fr/themode/minestom/item/ItemStack.java | 42 ++++++++++++--- .../fr/themode/minestom/item/Material.java | 42 +++++++++++++++ .../listener/BlockPlacementListener.java | 13 ++--- .../listener/PlayerDiggingListener.java | 5 ++ .../minestom/listener/UseItemListener.java | 6 +++ .../themode/minestom/net/PacketProcessor.java | 2 +- .../packet/client/login/LoginStartPacket.java | 22 ++++---- .../packet/server/play/BlockActionPacket.java | 27 ++++++++++ .../packet/server/play/ChunkDataPacket.java | 54 +++++++++++++++---- .../minestom/net/player/PlayerConnection.java | 15 +++--- .../themode/minestom/utils/BlockPosition.java | 7 +++ .../themode/minestom/utils/PacketUtils.java | 22 ++++++++ .../java/fr/themode/minestom/utils/Utils.java | 32 ++++++++++- 28 files changed, 387 insertions(+), 95 deletions(-) create mode 100644 src/main/java/fr/themode/minestom/item/Material.java create mode 100644 src/main/java/fr/themode/minestom/net/packet/server/play/BlockActionPacket.java create mode 100644 src/main/java/fr/themode/minestom/utils/PacketUtils.java diff --git a/src/main/java/fr/themode/minestom/Main.java b/src/main/java/fr/themode/minestom/Main.java index 608d2ab8b..bd97c7007 100644 --- a/src/main/java/fr/themode/minestom/Main.java +++ b/src/main/java/fr/themode/minestom/Main.java @@ -55,15 +55,16 @@ public class Main { public void onDisconnect(Server server, Connection connection) { System.out.println("A DISCONNECTION"); if (packetProcessor.hasPlayerConnection(connection)) { - if (connectionManager.getPlayer(packetProcessor.getPlayerConnection(connection)) != null) { - Player player = connectionManager.getPlayer(packetProcessor.getPlayerConnection(connection)); - player.remove(); + Player player = connectionManager.getPlayer(packetProcessor.getPlayerConnection(connection)); + if (player != null) { Instance instance = player.getInstance(); if (instance != null) { instance.removeEntity(player); } + player.remove(); + connectionManager.removePlayer(packetProcessor.getPlayerConnection(connection)); } packetProcessor.removePlayerConnection(connection); @@ -112,6 +113,7 @@ public class Main { // Sleep until next tick long sleepTime = (tickDistance - (System.nanoTime() - currentTime)) / 1000000; + sleepTime = Math.max(1, sleepTime); //String perfMessage = "Online: " + getConnectionManager().getOnlinePlayers().size() + " Tick time: " + (TICK_MS - sleepTime) + " ms"; //getConnectionManager().getOnlinePlayers().forEach(player -> player.sendMessage(perfMessage)); diff --git a/src/main/java/fr/themode/minestom/Viewable.java b/src/main/java/fr/themode/minestom/Viewable.java index beea126a4..5be1a9502 100644 --- a/src/main/java/fr/themode/minestom/Viewable.java +++ b/src/main/java/fr/themode/minestom/Viewable.java @@ -18,14 +18,30 @@ public interface Viewable { } default void sendPacketToViewers(ServerPacket packet) { + if (getViewers().isEmpty()) + return; + + //Packet p = PacketUtils.writePacket(packet); getViewers().forEach(player -> player.getPlayerConnection().sendPacket(packet)); } default void sendPacketsToViewers(ServerPacket... packets) { - getViewers().forEach(player -> { - for (ServerPacket packet : packets) - player.getPlayerConnection().sendPacket(packet); - }); + if (getViewers().isEmpty()) + return; + + for (ServerPacket packet : packets) { + //Packet p = PacketUtils.writePacket(packet); + getViewers().forEach(player -> player.getPlayerConnection().sendPacket(packet)); + } + } + + default void sendPacketToViewersAndSelf(ServerPacket packet) { + if (this instanceof Player) { + //Packet p = PacketUtils.writePacket(packet); + ((Player) this).getPlayerConnection().sendPacket(packet); + if (!getViewers().isEmpty()) + getViewers().forEach(player -> player.getPlayerConnection().sendPacket(packet)); + } } } diff --git a/src/main/java/fr/themode/minestom/entity/Entity.java b/src/main/java/fr/themode/minestom/entity/Entity.java index eb53e633f..c46a3b554 100644 --- a/src/main/java/fr/themode/minestom/entity/Entity.java +++ b/src/main/java/fr/themode/minestom/entity/Entity.java @@ -93,6 +93,8 @@ public abstract class Entity implements Viewable { if (isChunkUnloaded(position.getX(), position.getZ())) return; + refreshPosition(position.getX(), position.getY(), position.getZ()); + refreshView(position.getYaw(), position.getPitch()); EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket(); entityTeleportPacket.entityId = getEntityId(); entityTeleportPacket.position = position; @@ -289,13 +291,21 @@ public abstract class Entity implements Viewable { this.scheduledRemoveTime = System.currentTimeMillis() + delay; } + public EntityMetaDataPacket getMetadataPacket() { + EntityMetaDataPacket metaDataPacket = new EntityMetaDataPacket(); + metaDataPacket.entityId = getEntityId(); + metaDataPacket.data = getMetadataBuffer(); + return metaDataPacket; + } + public Buffer getMetadataBuffer() { Buffer buffer = Buffer.create(); fillMetadataIndex(buffer, 0); + fillMetadataIndex(buffer, 1); return buffer; } - protected void sendMetadata(int index) { + private void sendMetadata(int index) { Buffer buffer = Buffer.create(); fillMetadataIndex(buffer, index); diff --git a/src/main/java/fr/themode/minestom/entity/EntityCreature.java b/src/main/java/fr/themode/minestom/entity/EntityCreature.java index dfbc35dec..fc62a7bb5 100644 --- a/src/main/java/fr/themode/minestom/entity/EntityCreature.java +++ b/src/main/java/fr/themode/minestom/entity/EntityCreature.java @@ -1,6 +1,5 @@ package fr.themode.minestom.entity; -import fr.themode.minestom.net.packet.server.play.EntityMetaDataPacket; import fr.themode.minestom.net.packet.server.play.EntityPacket; import fr.themode.minestom.net.packet.server.play.EntityRelativeMovePacket; import fr.themode.minestom.net.packet.server.play.SpawnMobPacket; @@ -51,11 +50,8 @@ public abstract class EntityCreature extends LivingEntity { spawnMobPacket.entityType = getEntityType(); spawnMobPacket.position = getPosition(); spawnMobPacket.headPitch = 0; - EntityMetaDataPacket entityMetaDataPacket = new EntityMetaDataPacket(); - entityMetaDataPacket.entityId = getEntityId(); - entityMetaDataPacket.data = getMetadataBuffer(); playerConnection.sendPacket(entityPacket); playerConnection.sendPacket(spawnMobPacket); - playerConnection.sendPacket(entityMetaDataPacket); + playerConnection.sendPacket(getMetadataPacket()); } } diff --git a/src/main/java/fr/themode/minestom/entity/EntityManager.java b/src/main/java/fr/themode/minestom/entity/EntityManager.java index eaf61acfa..01a43ea82 100644 --- a/src/main/java/fr/themode/minestom/entity/EntityManager.java +++ b/src/main/java/fr/themode/minestom/entity/EntityManager.java @@ -17,6 +17,7 @@ public class EntityManager { private ExecutorService creaturesPool = Executors.newFixedThreadPool(2); private ExecutorService playersPool = Executors.newFixedThreadPool(2); + // TODO API for custom thread division ( public void update() { for (Instance instance : instanceManager.getInstances()) { testTick2(instance); // TODO optimize update engine for when there are too many entities on one chunk diff --git a/src/main/java/fr/themode/minestom/entity/LivingEntity.java b/src/main/java/fr/themode/minestom/entity/LivingEntity.java index 453058f9a..340c24a5b 100644 --- a/src/main/java/fr/themode/minestom/entity/LivingEntity.java +++ b/src/main/java/fr/themode/minestom/entity/LivingEntity.java @@ -1,12 +1,40 @@ package fr.themode.minestom.entity; +import fr.adamaq01.ozao.net.Buffer; + // TODO attributes https://wiki.vg/Protocol#Entity_Properties public abstract class LivingEntity extends Entity { protected boolean onGround; + private boolean isHandActive; + private boolean activeHand; + private boolean riptideSpinAttack; + public LivingEntity(int entityType) { super(entityType); } + @Override + public Buffer getMetadataBuffer() { + Buffer buffer = super.getMetadataBuffer(); + buffer.putByte((byte) 7); + buffer.putByte(METADATA_BYTE); + byte activeHandValue = 0; + if (isHandActive) { + activeHandValue += 1; + if (activeHand) + activeHandValue += 2; + if (riptideSpinAttack) + activeHandValue += 4; + } + buffer.putByte(activeHandValue); + return buffer; + } + + public void refreshActiveHand(boolean isHandActive, boolean offHand, boolean riptideSpinAttack) { + this.isHandActive = isHandActive; + this.activeHand = offHand; + this.riptideSpinAttack = riptideSpinAttack; + } } diff --git a/src/main/java/fr/themode/minestom/entity/ObjectEntity.java b/src/main/java/fr/themode/minestom/entity/ObjectEntity.java index 6c1579714..b8069d880 100644 --- a/src/main/java/fr/themode/minestom/entity/ObjectEntity.java +++ b/src/main/java/fr/themode/minestom/entity/ObjectEntity.java @@ -1,7 +1,6 @@ package fr.themode.minestom.entity; import fr.themode.minestom.Viewable; -import fr.themode.minestom.net.packet.server.play.EntityMetaDataPacket; import fr.themode.minestom.net.packet.server.play.SpawnObjectPacket; import fr.themode.minestom.net.player.PlayerConnection; @@ -21,7 +20,7 @@ public abstract class ObjectEntity extends Entity implements Viewable { @Override public void addViewer(Player player) { - this.viewers.add(player); + super.addViewer(player); PlayerConnection playerConnection = player.getPlayerConnection(); SpawnObjectPacket spawnObjectPacket = new SpawnObjectPacket(); @@ -30,16 +29,13 @@ public abstract class ObjectEntity extends Entity implements Viewable { spawnObjectPacket.type = getEntityType(); spawnObjectPacket.position = getPosition(); spawnObjectPacket.data = getData(); - EntityMetaDataPacket entityMetaDataPacket = new EntityMetaDataPacket(); - entityMetaDataPacket.entityId = getEntityId(); - entityMetaDataPacket.data = getMetadataBuffer(); playerConnection.sendPacket(spawnObjectPacket); - playerConnection.sendPacket(entityMetaDataPacket); + playerConnection.sendPacket(getMetadataPacket()); } @Override public void removeViewer(Player player) { - this.viewers.remove(player); + super.removeViewer(player); } @Override diff --git a/src/main/java/fr/themode/minestom/entity/Player.java b/src/main/java/fr/themode/minestom/entity/Player.java index 71c71eaef..f9ae177e2 100644 --- a/src/main/java/fr/themode/minestom/entity/Player.java +++ b/src/main/java/fr/themode/minestom/entity/Player.java @@ -3,6 +3,7 @@ package fr.themode.minestom.entity; import fr.themode.minestom.bossbar.BossBar; import fr.themode.minestom.chat.Chat; import fr.themode.minestom.event.AttackEvent; +import fr.themode.minestom.event.BlockPlaceEvent; import fr.themode.minestom.event.PickupItemEvent; import fr.themode.minestom.instance.Chunk; import fr.themode.minestom.instance.CustomBlock; @@ -43,6 +44,7 @@ public class Player extends LivingEntity { private CustomBlock targetCustomBlock; private BlockPosition targetBlockPosition; private long targetBlockTime; + private byte targetLastStage; private Set bossBars = new CopyOnWriteArraySet<>(); @@ -91,6 +93,10 @@ public class Player extends LivingEntity { updateHealthPacket.foodSaturation = 0; playerConnection.sendPacket(updateHealthPacket);*/ }); + + setEventCallback(BlockPlaceEvent.class, event -> { + sendMessage("Placed block!"); + }); } @Override @@ -108,7 +114,10 @@ public class Player extends LivingEntity { int animationCount = 10; long since = System.currentTimeMillis() - targetBlockTime; byte stage = (byte) (since / (timeBreak / animationCount)); - sendBlockBreakAnimation(targetBlockPosition, stage);// TODO send to all near players + if (stage != targetLastStage) { + sendBlockBreakAnimation(targetBlockPosition, stage); + } + this.targetLastStage = stage; if (stage > 9) { instance.breakBlock(this, targetBlockPosition, targetCustomBlock); resetTargetBlock(); @@ -138,8 +147,7 @@ public class Player extends LivingEntity { collectItemPacket.collectedEntityId = itemEntity.getEntityId(); collectItemPacket.collectorEntityId = getEntityId(); collectItemPacket.pickupItemCount = item.getAmount(); - playerConnection.sendPacket(collectItemPacket); - sendPacketToViewers(collectItemPacket); + sendPacketToViewersAndSelf(collectItemPacket); objectEntity.remove(); } }); @@ -220,7 +228,7 @@ public class Player extends LivingEntity { for (Player viewer : getViewers()) { EntityTeleportPacket teleportPacket = new EntityTeleportPacket(); teleportPacket.entityId = viewer.getEntityId(); - teleportPacket.position = getPosition(); + teleportPacket.position = viewer.getPosition(); teleportPacket.onGround = viewer.onGround; playerConnection.sendPacket(teleportPacket); } @@ -264,8 +272,7 @@ public class Player extends LivingEntity { breakAnimationPacket.entityId = getEntityId() + 1; breakAnimationPacket.blockPosition = blockPosition; breakAnimationPacket.destroyStage = destroyStage; - playerConnection.sendPacket(breakAnimationPacket); - sendPacketToViewers(breakAnimationPacket); + sendPacketToViewersAndSelf(breakAnimationPacket); } public void sendMessage(String message) { @@ -275,13 +282,16 @@ public class Player extends LivingEntity { @Override public void teleport(Position position) { + if (isChunkUnloaded(position.getX(), position.getZ())) + return; + refreshPosition(position.getX(), position.getY(), position.getZ()); refreshView(position.getYaw(), position.getPitch()); PlayerPositionAndLookPacket positionAndLookPacket = new PlayerPositionAndLookPacket(); positionAndLookPacket.position = position; positionAndLookPacket.flags = 0x00; positionAndLookPacket.teleportId = 67; - getPlayerConnection().sendPacket(positionAndLookPacket); + playerConnection.sendPacket(positionAndLookPacket); } public String getUsername() { diff --git a/src/main/java/fr/themode/minestom/event/BlockPlaceEvent.java b/src/main/java/fr/themode/minestom/event/BlockPlaceEvent.java index 5ca20db46..ae2e4bac4 100644 --- a/src/main/java/fr/themode/minestom/event/BlockPlaceEvent.java +++ b/src/main/java/fr/themode/minestom/event/BlockPlaceEvent.java @@ -1,14 +1,22 @@ package fr.themode.minestom.event; +import fr.themode.minestom.utils.BlockPosition; + public class BlockPlaceEvent extends CancellableEvent { private short blockId; + private BlockPosition blockPosition; - public BlockPlaceEvent(short blockId) { + public BlockPlaceEvent(short blockId, BlockPosition blockPosition) { this.blockId = blockId; + this.blockPosition = blockPosition; } public short getBlockId() { return blockId; } + + public BlockPosition getBlockPosition() { + return blockPosition; + } } diff --git a/src/main/java/fr/themode/minestom/instance/BlockBatch.java b/src/main/java/fr/themode/minestom/instance/BlockBatch.java index fd9f6ee18..716ea8197 100644 --- a/src/main/java/fr/themode/minestom/instance/BlockBatch.java +++ b/src/main/java/fr/themode/minestom/instance/BlockBatch.java @@ -55,14 +55,14 @@ public class BlockBatch implements BlockModifier { for (Map.Entry> entry : data.entrySet()) { Chunk chunk = entry.getKey(); List dataList = entry.getValue(); - synchronized (chunk) { - batchesPool.submit(() -> { + batchesPool.submit(() -> { + synchronized (chunk) { for (BlockData data : dataList) { data.apply(chunk); } instance.sendChunkUpdate(chunk); // TODO partial chunk data - }); - } + } + }); } } diff --git a/src/main/java/fr/themode/minestom/instance/BlockModifier.java b/src/main/java/fr/themode/minestom/instance/BlockModifier.java index 1c51c4bdc..59ce41af8 100644 --- a/src/main/java/fr/themode/minestom/instance/BlockModifier.java +++ b/src/main/java/fr/themode/minestom/instance/BlockModifier.java @@ -1,8 +1,19 @@ package fr.themode.minestom.instance; +import fr.themode.minestom.utils.BlockPosition; + public interface BlockModifier { void setBlock(int x, int y, int z, short blockId); void setBlock(int x, int y, int z, String blockId); + + default void setBlock(BlockPosition blockPosition, short blockId) { + setBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockId); + } + + default void setBlock(BlockPosition blockPosition, String blockId) { + setBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockId); + } + } diff --git a/src/main/java/fr/themode/minestom/instance/Chunk.java b/src/main/java/fr/themode/minestom/instance/Chunk.java index 2759e6632..d95f76167 100644 --- a/src/main/java/fr/themode/minestom/instance/Chunk.java +++ b/src/main/java/fr/themode/minestom/instance/Chunk.java @@ -23,6 +23,9 @@ public class Chunk { private short[] blocksId = new short[CHUNK_SIZE]; private short[] customBlocks = new short[CHUNK_SIZE]; + // Block entities + private Set blockEntities = new CopyOnWriteArraySet<>(); + public Chunk(Biome biome, int chunkX, int chunkZ) { this.biome = biome; this.chunkX = chunkX; @@ -30,9 +33,7 @@ public class Chunk { } protected void setBlock(byte x, byte y, byte z, short blockId) { - int index = getIndex(x, y, z); - this.blocksId[index] = blockId; - this.customBlocks[index] = 0; + setBlock(x, y, z, blockId, (short) 0); } protected void setBlock(byte x, byte y, byte z, String blockId) { @@ -40,9 +41,18 @@ public class Chunk { if (customBlock == null) throw new IllegalArgumentException("The block " + blockId + " does not exist or isn't registered"); + setBlock(x, y, z, customBlock.getType(), customBlock.getId()); + } + + private void setBlock(byte x, byte y, byte z, short blockType, short customId) { int index = getIndex(x, y, z); - this.blocksId[index] = customBlock.getType(); - this.customBlocks[index] = customBlock.getId(); + this.blocksId[index] = blockType; + this.customBlocks[index] = customId; + if (isBlockEntity(blockType)) { + blockEntities.add(index); + } else { + blockEntities.remove(index); + } } public short getBlockId(byte x, byte y, byte z) { @@ -120,6 +130,15 @@ public class Chunk { return Collections.unmodifiableSet(players); } + private boolean isBlockEntity(short blockId) { + // TODO complete + return blockId == 2033; + } + + public Set getBlockEntities() { + return blockEntities; + } + private int getIndex(byte x, byte y, byte z) { short index = (short) (x & 0x000F); index |= (y << 4) & 0x0FF0; diff --git a/src/main/java/fr/themode/minestom/instance/Instance.java b/src/main/java/fr/themode/minestom/instance/Instance.java index 8b703ce91..ba5dcfad4 100644 --- a/src/main/java/fr/themode/minestom/instance/Instance.java +++ b/src/main/java/fr/themode/minestom/instance/Instance.java @@ -60,7 +60,7 @@ public class Instance implements BlockModifier { int y = blockPosition.getY(); int z = blockPosition.getZ(); setBlock(x, y, z, (short) 0); - ParticlePacket particlePacket = new ParticlePacket(); // TODO change by a proper particle API + ParticlePacket particlePacket = new ParticlePacket(); // TODO change to a proper particle API particlePacket.particleId = 3; // Block particle particlePacket.longDistance = false; particlePacket.x = x + 0.5f; @@ -107,7 +107,7 @@ public class Instance implements BlockModifier { } public Chunk getChunk(int chunkX, int chunkZ) { - return chunks.getOrDefault(getChunkKey(chunkX, chunkZ), null); + return chunks.get(getChunkKey(chunkX, chunkZ)); } public Chunk getChunkAt(double x, double z) { @@ -144,6 +144,7 @@ public class Instance implements BlockModifier { if (entity instanceof Player) { Player player = (Player) entity; sendChunks(player); + getObjectEntities().forEach(objectEntity -> objectEntity.addViewer(player)); getCreatures().forEach(entityCreature -> entityCreature.addViewer(player)); getPlayers().forEach(p -> p.addViewer(player)); } @@ -164,6 +165,13 @@ public class Instance implements BlockModifier { destroyEntitiesPacket.entityIds = new int[]{entity.getEntityId()}; entity.getViewers().forEach(p -> p.getPlayerConnection().sendPacket(destroyEntitiesPacket)); // TODO destroy batch + } else { + // TODO optimize (cache all entities that the player see) + Player player = (Player) entity; + getObjectEntities().forEach(objectEntity -> objectEntity.removeViewer(player)); + getCreatures().forEach(entityCreature -> entityCreature.removeViewer(player)); + getPlayers().forEach(p -> p.removeViewer(player)); + } Chunk chunk = getChunkAt(entity.getPosition()); @@ -190,7 +198,7 @@ public class Instance implements BlockModifier { ChunkDataPacket chunkDataPacket = new ChunkDataPacket(); chunkDataPacket.fullChunk = false; chunkDataPacket.chunk = chunk; - player.getPlayerConnection().sendPacket(chunkDataPacket); + player.getPlayerConnection().sendPacket(chunkDataPacket); // TODO write packet buffer in another thread (Chunk packets are heavy) } protected Chunk createChunk(int chunkX, int chunkZ) { @@ -216,7 +224,7 @@ public class Instance implements BlockModifier { ChunkDataPacket chunkDataPacket = new ChunkDataPacket(); chunkDataPacket.fullChunk = false; chunkDataPacket.chunk = chunk; - getPlayers().forEach(player -> player.getPlayerConnection().sendPacket(chunkDataPacket)); + getPlayers().forEach(player -> player.getPlayerConnection().sendPacket(chunkDataPacket)); // TODO write packet buffer in another thread (Chunk packets are heavy) } private void sendChunks(Player player) { @@ -224,7 +232,7 @@ public class Instance implements BlockModifier { chunkDataPacket.fullChunk = true; for (Chunk chunk : getChunks()) { chunkDataPacket.chunk = chunk; - player.getPlayerConnection().sendPacket(chunkDataPacket); + player.getPlayerConnection().sendPacket(chunkDataPacket); // TODO write packet buffer in another thread (Chunk packets are heavy) } } diff --git a/src/main/java/fr/themode/minestom/instance/demo/ChunkGeneratorDemo.java b/src/main/java/fr/themode/minestom/instance/demo/ChunkGeneratorDemo.java index 4a9efe460..5a7bce95e 100644 --- a/src/main/java/fr/themode/minestom/instance/demo/ChunkGeneratorDemo.java +++ b/src/main/java/fr/themode/minestom/instance/demo/ChunkGeneratorDemo.java @@ -14,7 +14,7 @@ public class ChunkGeneratorDemo extends ChunkGenerator { public void generateChunkData(ChunkBatch batch, int chunkX, int chunkZ) { for (byte x = 0; x < 16; x++) for (byte z = 0; z < 16; z++) { - if (random.nextInt(2) == 1) { + if (random.nextInt(10) > 5) { batch.setBlock(x, (byte) 4, z, (short) 10); } else { batch.setBlock(x, (byte) 4, z, "custom_block"); diff --git a/src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java b/src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java index 010d0d36c..80cef0c35 100644 --- a/src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java +++ b/src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java @@ -7,7 +7,7 @@ public class StoneBlock extends CustomBlock { @Override public short getType() { - return 117; + return 1; } @Override diff --git a/src/main/java/fr/themode/minestom/item/ItemStack.java b/src/main/java/fr/themode/minestom/item/ItemStack.java index 9e452fda6..75192e879 100644 --- a/src/main/java/fr/themode/minestom/item/ItemStack.java +++ b/src/main/java/fr/themode/minestom/item/ItemStack.java @@ -4,35 +4,61 @@ public class ItemStack { public static final ItemStack AIR_ITEM = new ItemStack(0, (byte) 1); - private int itemId; + private Material material; private byte amount; - public ItemStack(int itemId, byte amount) { - this.itemId = itemId; + private String displayName; + private boolean unbreakable; + + public ItemStack(Material material, byte amount) { + this.material = material; this.amount = amount; } + public ItemStack(int id, byte amount) { + this(Material.fromId(id), amount); + } + public boolean isAir() { - return itemId == 0; + return material == Material.AIR; } public boolean isSimilar(ItemStack itemStack) { - return itemStack.getItemId() == itemId; + return itemStack.getMaterial() == material && itemStack.getDisplayName() == displayName && itemStack.isUnbreakable() == unbreakable; } public byte getAmount() { return amount; } - public int getItemId() { - return itemId; + public Material getMaterial() { + return material; } public void setAmount(byte amount) { this.amount = amount; } + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public boolean isUnbreakable() { + return unbreakable; + } + + public void setUnbreakable(boolean unbreakable) { + this.unbreakable = unbreakable; + } + public ItemStack clone() { - return new ItemStack(itemId, amount); + ItemStack itemStack = new ItemStack(material, amount); + itemStack.setDisplayName(displayName); + itemStack.setUnbreakable(unbreakable); + return itemStack; } } diff --git a/src/main/java/fr/themode/minestom/item/Material.java b/src/main/java/fr/themode/minestom/item/Material.java new file mode 100644 index 000000000..1cce67501 --- /dev/null +++ b/src/main/java/fr/themode/minestom/item/Material.java @@ -0,0 +1,42 @@ +package fr.themode.minestom.item; + +import java.util.HashMap; +import java.util.Map; + +public enum Material { + + AIR(0), + STONE(1), + BOW(525), + ARROW(526); + + private static Map idToMaterial = new HashMap<>(); + + static { + for (Material material : values()) { + idToMaterial.put(material.id, material); + } + } + + private int id; + + Material(int id) { + this.id = id; + } + + public static Material fromId(int id) { + return idToMaterial.get(id); + } + + public boolean isBlock() { + return false; // TODO + } + + public boolean isFood() { + return false; // TODO + } + + public int getId() { + return id; + } +} diff --git a/src/main/java/fr/themode/minestom/listener/BlockPlacementListener.java b/src/main/java/fr/themode/minestom/listener/BlockPlacementListener.java index 1b3dd25df..5f9348c8d 100644 --- a/src/main/java/fr/themode/minestom/listener/BlockPlacementListener.java +++ b/src/main/java/fr/themode/minestom/listener/BlockPlacementListener.java @@ -22,24 +22,17 @@ public class BlockPlacementListener { int offsetY = blockFace == ClientPlayerDiggingPacket.BlockFace.BOTTOM ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.TOP ? 1 : 0; int offsetZ = blockFace == ClientPlayerDiggingPacket.BlockFace.NORTH ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.SOUTH ? 1 : 0; - BlockPlaceEvent blockPlaceEvent = new BlockPlaceEvent((short) 10); + blockPosition.add(offsetX, offsetY, offsetZ); + BlockPlaceEvent blockPlaceEvent = new BlockPlaceEvent((short) 10, blockPosition); player.callEvent(BlockPlaceEvent.class, blockPlaceEvent); if (!blockPlaceEvent.isCancelled()) { - instance.setBlock(blockPosition.getX() + offsetX, blockPosition.getY() + offsetY, blockPosition.getZ() + offsetZ, "custom_block"); + instance.setBlock(blockPosition, "custom_block"); // TODO consume block in hand for survival players } else { Chunk chunk = instance.getChunkAt(blockPosition); instance.sendChunkUpdate(player, chunk); } player.getInventory().refreshSlot(player.getHeldSlot()); - /*Random random = new Random(); - BlockBatch blockBatch = instance.createBlockBatch(); - for (int x = -64; x < 64; x++) - for (int z = -64; z < 64; z++) { - if (random.nextInt(100) > 75) - blockBatch.setBlock(x, position.getY() + 1, z, new Block(1)); - } - blockBatch.flush();*/ } } diff --git a/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java b/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java index 7147d257c..1de8869f5 100644 --- a/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java +++ b/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java @@ -57,6 +57,11 @@ public class PlayerDiggingListener { } } break; + case UPDATE_ITEM_STATE: + // TODO check if is updatable item + //player.refreshActiveHand(false, false, false); + //player.sendPacketToViewers(player.getMetadataPacket()); + break; } } diff --git a/src/main/java/fr/themode/minestom/listener/UseItemListener.java b/src/main/java/fr/themode/minestom/listener/UseItemListener.java index 464e66210..658a89477 100644 --- a/src/main/java/fr/themode/minestom/listener/UseItemListener.java +++ b/src/main/java/fr/themode/minestom/listener/UseItemListener.java @@ -13,6 +13,12 @@ public class UseItemListener { Player.Hand hand = packet.hand; ItemStack itemStack = hand == Player.Hand.MAIN ? inventory.getItemInMainHand() : inventory.getItemInOffHand(); UseItemEvent useItemEvent = new UseItemEvent(hand, itemStack); + player.callEvent(UseItemEvent.class, useItemEvent); + + // TODO check if item in main or off hand is food or item with animation (bow/crossbow/riptide) + // TODO in material enum? + //player.refreshActiveHand(true, false, false); + //player.sendPacketToViewers(player.getMetadataPacket()); } } diff --git a/src/main/java/fr/themode/minestom/net/PacketProcessor.java b/src/main/java/fr/themode/minestom/net/PacketProcessor.java index f4af0d4e7..c271ee3bc 100644 --- a/src/main/java/fr/themode/minestom/net/PacketProcessor.java +++ b/src/main/java/fr/themode/minestom/net/PacketProcessor.java @@ -45,7 +45,7 @@ public class PacketProcessor { public void process(Connection connection, Packet packet) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { int id = packet.get(PACKET_ID_IDENTIFIER); if (!printBlackList.contains(id)) { - System.out.println("RECEIVED ID: " + id); + //System.out.println("RECEIVED ID: 0x" + Integer.toHexString(id)); } Buffer buffer = packet.getPayload(); connectionPlayerConnectionMap.get(connection); diff --git a/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java b/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java index a79b6e1c1..5e3484f60 100644 --- a/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java @@ -13,6 +13,7 @@ import fr.themode.minestom.instance.Instance; import fr.themode.minestom.instance.demo.ChunkGeneratorDemo; import fr.themode.minestom.inventory.PlayerInventory; import fr.themode.minestom.item.ItemStack; +import fr.themode.minestom.item.Material; import fr.themode.minestom.net.ConnectionManager; import fr.themode.minestom.net.ConnectionState; import fr.themode.minestom.net.packet.client.ClientPreplayPacket; @@ -30,17 +31,12 @@ import java.util.UUID; public class LoginStartPacket implements ClientPreplayPacket { - private String username; - - // Test - private static Instance instance; - static { ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo(); instance = Main.getInstanceManager().createInstance(); instance.setChunkGenerator(chunkGeneratorDemo); - int loopStart = -4; - int loopEnd = 4; + int loopStart = -2; + int loopEnd = 2; long time = System.currentTimeMillis(); for (int x = loopStart; x < loopEnd; x++) for (int z = loopStart; z < loopEnd; z++) { @@ -49,6 +45,11 @@ public class LoginStartPacket implements ClientPreplayPacket { System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms"); } + // Test + private static Instance instance; + + public String username; + @Override public void process(PlayerConnection connection, ConnectionManager connectionManager) { String property = "eyJ0aW1lc3RhbXAiOjE1NjU0ODMwODQwOTYsInByb2ZpbGVJZCI6ImFiNzBlY2I0MjM0NjRjMTRhNTJkN2EwOTE1MDdjMjRlIiwicHJvZmlsZU5hbWUiOiJUaGVNb2RlOTExIiwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2RkOTE2NzJiNTE0MmJhN2Y3MjA2ZTRjN2IwOTBkNzhlM2Y1ZDc2NDdiNWFmZDIyNjFhZDk4OGM0MWI2ZjcwYTEifX19"; @@ -128,16 +129,15 @@ public class LoginStartPacket implements ClientPreplayPacket { } PlayerInventory inventory = player.getInventory(); - for (int i = 0; i < 20; i++) { - inventory.addItemStack(new ItemStack(1, (byte) 64)); - } + inventory.addItemStack(new ItemStack(Material.BOW, (byte) 1)); + inventory.addItemStack(new ItemStack(Material.ARROW, (byte) 100)); /*Inventory inv = new Inventory(InventoryType.WINDOW_3X3, "Salut je suis le titre"); inv.setItemStack(0, new ItemStack(1, (byte) 1)); player.openInventory(inv); inv.setItemStack(1, new ItemStack(1, (byte) 2));*/ - BossBar bossBar = new BossBar("Le titre", BarColor.BLUE, BarDivision.SEGMENT_12); + BossBar bossBar = new BossBar("Bossbar Title", BarColor.BLUE, BarDivision.SEGMENT_12); bossBar.setProgress(0.75f); bossBar.addViewer(player); diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/BlockActionPacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/BlockActionPacket.java new file mode 100644 index 000000000..46cc3c64f --- /dev/null +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/BlockActionPacket.java @@ -0,0 +1,27 @@ +package fr.themode.minestom.net.packet.server.play; + +import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.net.packet.server.ServerPacket; +import fr.themode.minestom.utils.BlockPosition; +import fr.themode.minestom.utils.Utils; + +public class BlockActionPacket implements ServerPacket { + + public BlockPosition blockPosition; + public byte actionId; + public byte actionParam; + public int blockId; + + @Override + public void write(Buffer buffer) { + Utils.writePosition(buffer, blockPosition); + buffer.putByte(actionId); + buffer.putByte(actionParam); + Utils.writeVarInt(buffer, blockId); + } + + @Override + public int getId() { + return 0x0A; + } +} diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java index a4596b58e..afed275a9 100644 --- a/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java @@ -3,13 +3,16 @@ package fr.themode.minestom.net.packet.server.play; import fr.adamaq01.ozao.net.Buffer; import fr.themode.minestom.instance.Chunk; import fr.themode.minestom.net.packet.server.ServerPacket; +import fr.themode.minestom.utils.BlockPosition; import fr.themode.minestom.utils.Utils; import net.querz.nbt.CompoundTag; +import net.querz.nbt.DoubleTag; import net.querz.nbt.LongArrayTag; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Set; public class ChunkDataPacket implements ServerPacket { @@ -55,21 +58,43 @@ public class ChunkDataPacket implements ServerPacket { worldSurface[x + z * 16] = 5; } } - CompoundTag compound = new CompoundTag(); - compound.put("MOTION_BLOCKING", new LongArrayTag(Utils.encodeBlocks(motionBlocking, 9))); - compound.put("WORLD_SURFACE", new LongArrayTag(Utils.encodeBlocks(worldSurface, 9))); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try { - compound.serialize(new DataOutputStream(outputStream), 100); - } catch (IOException e) { - e.printStackTrace(); + + { + CompoundTag compound = new CompoundTag(); + compound.put("MOTION_BLOCKING", new LongArrayTag(Utils.encodeBlocks(motionBlocking, 9))); + compound.put("WORLD_SURFACE", new LongArrayTag(Utils.encodeBlocks(worldSurface, 9))); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try { + compound.serialize(new DataOutputStream(outputStream), 100); + } catch (IOException e) { + e.printStackTrace(); + } + byte[] data = outputStream.toByteArray(); + buffer.putBytes(data); } - byte[] data = outputStream.toByteArray(); - buffer.putBytes(data); Utils.writeVarInt(buffer, blocks.length()); buffer.putBuffer(blocks); - Utils.writeVarInt(buffer, 0); + + // Block entities + Set blockEntities = chunk.getBlockEntities(); + Utils.writeVarInt(buffer, blockEntities.size()); + + for (Integer index : blockEntities) { + BlockPosition blockPosition = indexToBlockPosition(index); + CompoundTag blockEntity = new CompoundTag(); + blockEntity.put("x", new DoubleTag(blockPosition.getX())); + blockEntity.put("y", new DoubleTag(blockPosition.getY())); + blockEntity.put("z", new DoubleTag(blockPosition.getZ())); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + try { + blockEntity.serialize(new DataOutputStream(os), 100); + } catch (IOException e) { + e.printStackTrace(); + } + byte[] d = os.toByteArray(); + buffer.putBytes(d); + } } private short[] getSection(Chunk chunk, int section) { @@ -85,6 +110,13 @@ public class ChunkDataPacket implements ServerPacket { return blocks; } + private BlockPosition indexToBlockPosition(int index) { + byte z = (byte) (index >> 12 & 0xF); + byte y = (byte) (index >> 4 & 0xFF); + byte x = (byte) (index >> 0 & 0xF); + return new BlockPosition(x + 16 * chunk.getChunkX(), y, z + 16 * chunk.getChunkZ()); + } + @Override public int getId() { return 0x21; diff --git a/src/main/java/fr/themode/minestom/net/player/PlayerConnection.java b/src/main/java/fr/themode/minestom/net/player/PlayerConnection.java index a442a06b3..b9ff66e61 100644 --- a/src/main/java/fr/themode/minestom/net/player/PlayerConnection.java +++ b/src/main/java/fr/themode/minestom/net/player/PlayerConnection.java @@ -1,12 +1,10 @@ package fr.themode.minestom.net.player; -import fr.adamaq01.ozao.net.Buffer; import fr.adamaq01.ozao.net.packet.Packet; import fr.adamaq01.ozao.net.server.Connection; import fr.themode.minestom.net.ConnectionState; import fr.themode.minestom.net.packet.server.ServerPacket; - -import static fr.themode.minestom.net.protocol.MinecraftProtocol.PACKET_ID_IDENTIFIER; +import fr.themode.minestom.utils.PacketUtils; public class PlayerConnection { @@ -18,15 +16,14 @@ public class PlayerConnection { this.connectionState = ConnectionState.UNKNOWN; } - public void sendPacket(ServerPacket serverPacket) { - Packet packet = Packet.create(); - Buffer buffer = packet.getPayload(); - serverPacket.write(buffer); - packet.put(PACKET_ID_IDENTIFIER, serverPacket.getId()); - + public void sendPacket(Packet packet) { this.connection.sendPacket(packet); } + public void sendPacket(ServerPacket serverPacket) { + sendPacket(PacketUtils.writePacket(serverPacket)); + } + public Connection getConnection() { return connection; } diff --git a/src/main/java/fr/themode/minestom/utils/BlockPosition.java b/src/main/java/fr/themode/minestom/utils/BlockPosition.java index b72b67956..8f88fb080 100644 --- a/src/main/java/fr/themode/minestom/utils/BlockPosition.java +++ b/src/main/java/fr/themode/minestom/utils/BlockPosition.java @@ -10,6 +10,12 @@ public class BlockPosition { this.z = z; } + public void add(int x, int y, int z) { + this.x += x; + this.y += y; + this.z += z; + } + public int getX() { return x; } @@ -34,6 +40,7 @@ public class BlockPosition { this.z = z; } + @Override public String toString() { return "Position[" + x + ":" + y + ":" + z + "]"; diff --git a/src/main/java/fr/themode/minestom/utils/PacketUtils.java b/src/main/java/fr/themode/minestom/utils/PacketUtils.java new file mode 100644 index 000000000..d8149d3ac --- /dev/null +++ b/src/main/java/fr/themode/minestom/utils/PacketUtils.java @@ -0,0 +1,22 @@ +package fr.themode.minestom.utils; + +import fr.adamaq01.ozao.net.Buffer; +import fr.adamaq01.ozao.net.packet.Packet; +import fr.themode.minestom.net.packet.server.ServerPacket; + +import static fr.themode.minestom.net.protocol.MinecraftProtocol.PACKET_ID_IDENTIFIER; + +public class PacketUtils { + + public static Packet writePacket(ServerPacket serverPacket) { + int id = serverPacket.getId(); + Packet packet = Packet.create(); + Buffer buffer = packet.getPayload(); + serverPacket.write(buffer); + /*if (id != 40 && id != 64) + System.out.println("ID: 0x" + Integer.toHexString(id));*/ + packet.put(PACKET_ID_IDENTIFIER, id); + return packet; + } + +} diff --git a/src/main/java/fr/themode/minestom/utils/Utils.java b/src/main/java/fr/themode/minestom/utils/Utils.java index 13676cd46..783867d27 100644 --- a/src/main/java/fr/themode/minestom/utils/Utils.java +++ b/src/main/java/fr/themode/minestom/utils/Utils.java @@ -1,6 +1,7 @@ package fr.themode.minestom.utils; import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.chat.Chat; import fr.themode.minestom.item.ItemStack; import java.io.UnsupportedEncodingException; @@ -145,10 +146,39 @@ public class Utils { buffer.putBoolean(false); } else { buffer.putBoolean(true); - Utils.writeVarInt(buffer, itemStack.getItemId()); + Utils.writeVarInt(buffer, itemStack.getMaterial().getId()); buffer.putByte(itemStack.getAmount()); + + buffer.putByte((byte) 0x0A); // Compound + buffer.putShort((short) 0); + + // Unbreakable + if (itemStack.isUnbreakable()) { + buffer.putByte((byte) 0x03); // Integer + buffer.putString("Unbreakable"); + buffer.putInt(1); + } + + // Display + buffer.putByte((byte) 0x0A); // Compound + buffer.putString("display"); + + if (itemStack.getDisplayName() != null) { + buffer.putByte((byte) 0x08); + buffer.putString("Name"); + buffer.putString(Chat.rawText(itemStack.getDisplayName())); + } + + // TODO lore + buffer.putByte((byte) 0x08); + buffer.putString("Lore"); + buffer.putString(Chat.rawText("a line")); + + buffer.putByte((byte) 0); // End display compound + buffer.putByte((byte) 0); // End nbt TODO } + } public static void writeBlocks(Buffer buffer, short[] blocksId, int bitsPerEntry) {