diff --git a/src/main/java/fr/themode/minestom/Viewable.java b/src/main/java/fr/themode/minestom/Viewable.java index d43cc886c..bb8d86f7a 100644 --- a/src/main/java/fr/themode/minestom/Viewable.java +++ b/src/main/java/fr/themode/minestom/Viewable.java @@ -4,6 +4,7 @@ import fr.themode.minestom.entity.Player; import fr.themode.minestom.net.PacketWriterUtils; import fr.themode.minestom.net.packet.server.ServerPacket; +import java.util.HashSet; import java.util.Set; public interface Viewable { @@ -19,60 +20,31 @@ public interface Viewable { } default void sendPacketToViewers(ServerPacket packet) { - if (getViewers().isEmpty()) - return; - - PacketWriterUtils.writeCallbackPacket(packet, buffer -> { - int size = getViewers().size(); - if (size == 0) - return; - buffer.getData().retain(size).markReaderIndex(); - for (Player viewer : getViewers()) { - viewer.getPlayerConnection().writeUnencodedPacket(buffer); - buffer.getData().resetReaderIndex(); - } - }); + PacketWriterUtils.writeAndSend(getViewers(), packet); } default void sendPacketsToViewers(ServerPacket... packets) { - if (getViewers().isEmpty()) - return; - for (ServerPacket packet : packets) { - PacketWriterUtils.writeCallbackPacket(packet, buffer -> { - int size = getViewers().size(); - if (size == 0) - return; - buffer.getData().retain(size).markReaderIndex(); - for (Player viewer : getViewers()) { - viewer.getPlayerConnection().writeUnencodedPacket(buffer); - buffer.getData().resetReaderIndex(); - } - }); + PacketWriterUtils.writeAndSend(getViewers(), packet); } } default void sendPacketToViewersAndSelf(ServerPacket packet) { if (this instanceof Player) { - UNSAFE_sendPacketToViewersAndSelf(packet); + if (getViewers().isEmpty()) { + ((Player) this).getPlayerConnection().sendPacket(packet); + } else { + UNSAFE_sendPacketToViewersAndSelf(packet); + } } else { sendPacketToViewers(packet); } } private void UNSAFE_sendPacketToViewersAndSelf(ServerPacket packet) { - PacketWriterUtils.writeCallbackPacket(packet, buffer -> { - int size = getViewers().size(); - buffer.getData().retain(size + 1).markReaderIndex(); - ((Player) this).getPlayerConnection().writeUnencodedPacket(buffer); - buffer.getData().resetReaderIndex(); - if (size != 0) { - for (Player viewer : getViewers()) { - buffer.getData().resetReaderIndex(); - viewer.getPlayerConnection().writeUnencodedPacket(buffer); - } - } - }); + Set recipients = new HashSet<>(getViewers()); + recipients.add((Player) this); + PacketWriterUtils.writeAndSend(recipients, packet); } } diff --git a/src/main/java/fr/themode/minestom/entity/Entity.java b/src/main/java/fr/themode/minestom/entity/Entity.java index 806911a1a..1f9f86252 100644 --- a/src/main/java/fr/themode/minestom/entity/Entity.java +++ b/src/main/java/fr/themode/minestom/entity/Entity.java @@ -46,7 +46,6 @@ public abstract class Entity implements Viewable, DataContainer { protected Entity vehicle; // Velocity - // TODO gravity implementation for entity other than players protected Vector velocity = new Vector(); // Movement in block per second protected float gravityDragPerTick; private int gravityTickCounter; @@ -247,8 +246,9 @@ public abstract class Entity implements Viewable, DataContainer { gravityTickCounter = 0; } - if (this instanceof EntityCreature) // Objects are automatically updated client side + if (this instanceof EntityCreature) { teleport(getPosition()); + } } } @@ -444,7 +444,7 @@ public abstract class Entity implements Viewable, DataContainer { position.setZ(z); for (Entity passenger : getPassengers()) { - passenger.position = getPosition().clone(); + passenger.refreshPosition(x, y, z); } Instance instance = getInstance(); @@ -455,17 +455,8 @@ public abstract class Entity implements Viewable, DataContainer { synchronized (instance) { instance.removeEntityFromChunk(this, lastChunk); instance.addEntityToChunk(this, newChunk); - - for (Entity passenger : getPassengers()) { - instance.removeEntityFromChunk(passenger, lastChunk); - instance.addEntityToChunk(passenger, newChunk); - } } updateView(this, lastChunk, newChunk); - for (Entity passenger : getPassengers()) { - updateView(passenger, lastChunk, newChunk); - } - } } } @@ -647,7 +638,9 @@ public abstract class Entity implements Viewable, DataContainer { entityTeleportPacket.position = getPosition(); entityTeleportPacket.onGround = isOnGround(); sendPacketToViewers(entityTeleportPacket); - sendPacketToViewers(getPassengersPacket()); + + if (!passengers.isEmpty()) + sendPacketToViewers(getPassengersPacket()); } private void fillAirTickMetaData(Buffer buffer) { diff --git a/src/main/java/fr/themode/minestom/entity/EntityManager.java b/src/main/java/fr/themode/minestom/entity/EntityManager.java index 850cc0996..c8a38d080 100644 --- a/src/main/java/fr/themode/minestom/entity/EntityManager.java +++ b/src/main/java/fr/themode/minestom/entity/EntityManager.java @@ -13,6 +13,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; public class EntityManager { @@ -43,18 +44,28 @@ public class EntityManager { Position position = playerCache.getPosition(); long[] visibleChunks = ChunkUtils.getChunksInRange(position, Main.CHUNK_VIEW_DISTANCE); - for (int i = 0; i < visibleChunks.length; i++) { + int length = visibleChunks.length; + + AtomicInteger counter = new AtomicInteger(0); + + for (int i = 0; i < length; i++) { int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunks[i]); int chunkX = chunkPos[0]; int chunkZ = chunkPos[1]; - boolean isLast = i == visibleChunks.length - 1; - Consumer callback = isLast ? chunk -> { - playerCache.spawned = true; - playerCache.setInstance(spawningInstance); - PlayerSpawnEvent spawnEvent = new PlayerSpawnEvent(); - playerCache.callEvent(PlayerSpawnEvent.class, spawnEvent); - playerCache.updateViewPosition(chunk); - } : null; + Consumer callback = (chunk) -> { + boolean isLast = counter.get() == length - 1; + if (isLast) { + // This is the last chunk to be loaded, spawn player + playerCache.spawned = true; + playerCache.setInstance(spawningInstance); + PlayerSpawnEvent spawnEvent = new PlayerSpawnEvent(); + playerCache.callEvent(PlayerSpawnEvent.class, spawnEvent); + playerCache.updateViewPosition(chunk); + } else { + // Increment the counter of current loaded chunks + counter.incrementAndGet(); + } + }; // WARNING: if auto load is disabled and no chunks are loaded beforehand, player will be stuck. spawningInstance.loadChunk(chunkX, chunkZ, callback); diff --git a/src/main/java/fr/themode/minestom/entity/Player.java b/src/main/java/fr/themode/minestom/entity/Player.java index d5ccecaa5..cce4914a3 100644 --- a/src/main/java/fr/themode/minestom/entity/Player.java +++ b/src/main/java/fr/themode/minestom/entity/Player.java @@ -115,7 +115,7 @@ public class Player extends LivingEntity { sendMessage("You attacked an entity!"); } else if (entity instanceof Player) { Player player = (Player) entity; - Vector velocity = getPosition().clone().getDirection().multiply(6); + Vector velocity = getPosition().clone().getDirection().multiply(4); velocity.setY(3.5f); player.setVelocity(velocity, 150); player.damage(2); @@ -155,7 +155,7 @@ public class Player extends LivingEntity { setEventCallback(PlayerSpawnEvent.class, event -> { System.out.println("SPAWN"); - setGameMode(GameMode.SURVIVAL); + setGameMode(GameMode.CREATIVE); teleport(new Position(0, 66, 0)); /*ChickenCreature chickenCreature = new ChickenCreature(); @@ -330,6 +330,8 @@ public class Player extends LivingEntity { @Override public void addViewer(Player player) { + if (player == this) + return; super.addViewer(player); PlayerConnection connection = player.getPlayerConnection(); String property = "eyJ0aW1lc3RhbXAiOjE1NjU0ODMwODQwOTYsInByb2ZpbGVJZCI6ImFiNzBlY2I0MjM0NjRjMTRhNTJkN2EwOTE1MDdjMjRlIiwicHJvZmlsZU5hbWUiOiJUaGVNb2RlOTExIiwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2RkOTE2NzJiNTE0MmJhN2Y3MjA2ZTRjN2IwOTBkNzhlM2Y1ZDc2NDdiNWFmZDIyNjFhZDk4OGM0MWI2ZjcwYTEifX19"; @@ -355,6 +357,8 @@ public class Player extends LivingEntity { @Override public void removeViewer(Player player) { + if (player == this) + return; super.removeViewer(player); PlayerInfoPacket playerInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.REMOVE_PLAYER); playerInfoPacket.playerInfos.add(new PlayerInfoPacket.RemovePlayer(getUuid())); @@ -523,7 +527,7 @@ public class Player extends LivingEntity { int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunks[index]); instance.loadOptionalChunk(chunkPos[0], chunkPos[1], chunk -> { if (chunk == null) { - return; // Cannot load chunk (auto load not enabled) + return; // Cannot load chunk (auto load is not enabled) } instance.sendChunk(this, chunk); if (isFar && isLast) { @@ -536,8 +540,6 @@ public class Player extends LivingEntity { @Override public void teleport(Position position, Runnable callback) { super.teleport(position, () -> { - if (!instance.hasEnabledAutoChunkLoad() && ChunkUtils.isChunkUnloaded(instance, position.getX(), position.getZ())) - return; updatePlayerPosition(); if (callback != null) callback.run(); diff --git a/src/main/java/fr/themode/minestom/listener/PlayerPositionListener.java b/src/main/java/fr/themode/minestom/listener/PlayerPositionListener.java index 3bb0a4f59..0133c1b5d 100644 --- a/src/main/java/fr/themode/minestom/listener/PlayerPositionListener.java +++ b/src/main/java/fr/themode/minestom/listener/PlayerPositionListener.java @@ -42,7 +42,6 @@ public class PlayerPositionListener { } private static void processMovement(Player player, float x, float y, float z, Runnable runnable) { - //System.out.println("MOVEMENT PACKET " + Math.round(x) + ":" + Math.round(y) + ":" + Math.round(z)); boolean chunkTest = ChunkUtils.isChunkUnloaded(player.getInstance(), x, z); if (chunkTest) { player.teleport(player.getPosition()); diff --git a/src/main/java/fr/themode/minestom/net/PacketWriterUtils.java b/src/main/java/fr/themode/minestom/net/PacketWriterUtils.java index c8af7dde9..422f9bc47 100644 --- a/src/main/java/fr/themode/minestom/net/PacketWriterUtils.java +++ b/src/main/java/fr/themode/minestom/net/PacketWriterUtils.java @@ -3,9 +3,11 @@ package fr.themode.minestom.net; import fr.adamaq01.ozao.net.Buffer; import fr.adamaq01.ozao.net.packet.Packet; import fr.themode.minestom.Main; +import fr.themode.minestom.entity.Player; import fr.themode.minestom.net.packet.server.ServerPacket; import fr.themode.minestom.utils.PacketUtils; +import java.util.Collection; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Consumer; @@ -21,4 +23,21 @@ public class PacketWriterUtils { }); } + public static void writeAndSend(Collection players, ServerPacket serverPacket) { + batchesPool.execute(() -> { + Packet p = PacketUtils.writePacket(serverPacket); + Buffer encoded = PacketUtils.encode(p); + + + int size = players.size(); + if (size == 0) + return; + encoded.getData().retain(size).markReaderIndex(); + for (Player player : players) { + player.getPlayerConnection().writeUnencodedPacket(encoded); + encoded.getData().resetReaderIndex(); + } + }); + } + } diff --git a/src/main/java/fr/themode/minestom/utils/PacketUtils.java b/src/main/java/fr/themode/minestom/utils/PacketUtils.java index 09d3a255d..7f8b20c56 100644 --- a/src/main/java/fr/themode/minestom/utils/PacketUtils.java +++ b/src/main/java/fr/themode/minestom/utils/PacketUtils.java @@ -29,6 +29,7 @@ public class PacketUtils { public static Buffer encode(Packet packet) { Buffer buffer = Buffer.create(); Buffer idAndPayload = Buffer.create(); + writeVarInt(idAndPayload, packet.get(PACKET_ID_IDENTIFIER)); idAndPayload.putBuffer(packet.getPayload()); writeVarInt(buffer, idAndPayload.length()); diff --git a/src/main/java/fr/themode/minestom/utils/Utils.java b/src/main/java/fr/themode/minestom/utils/Utils.java index 95ee0fb6f..edc3c1639 100644 --- a/src/main/java/fr/themode/minestom/utils/Utils.java +++ b/src/main/java/fr/themode/minestom/utils/Utils.java @@ -5,7 +5,6 @@ import fr.themode.minestom.chat.Chat; import fr.themode.minestom.item.ItemStack; import java.io.UnsupportedEncodingException; -import java.util.UUID; public class Utils { @@ -132,11 +131,6 @@ public class Utils { return SerializerUtils.longToBlockPosition(buffer.getLong()); } - public static void writeUuid(Buffer buffer, UUID uuid) { - buffer.putLong(uuid.getMostSignificantBits()); - buffer.putLong(uuid.getLeastSignificantBits()); - } - public static void writeItemStack(Buffer buffer, ItemStack itemStack) { if (itemStack == null) { buffer.putBoolean(false);