From 78937a6b0b707804bf993bad7c52f89fba18a522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9meth=20Noel?= Date: Wed, 5 May 2021 00:48:29 +0200 Subject: [PATCH 01/11] Updated javadoc --- src/main/java/net/minestom/server/entity/Entity.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index cbc3f25b8..87b539f61 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -683,8 +683,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer, * (performed using {@link #synchronizePosition()}) *
  • {@link EntityPositionAndRotationPacket} if {@code positionChange && viewChange}
  • *
  • {@link EntityPositionPacket} if {@code positionChange}
  • - *
  • {@link EntityRotationPacket} if {@code viewChange} - * (performed using {@link #setView(float, float)})
  • + *
  • {@link EntityRotationPacket} and {@link EntityHeadLookPacket} if {@code viewChange}
  • * * In case of a player's position and/or view change an additional {@link PlayerPositionAndLookPacket} * is sent to self. From 5b41f3ffd0ae2cc6b5c71da776083ad9da05f9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9meth=20Noel?= Date: Wed, 5 May 2021 17:05:22 +0200 Subject: [PATCH 02/11] Spiral chunk loading --- .../server/utils/chunk/ChunkUtils.java | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java b/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java index eeefd3b64..687df6036 100644 --- a/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java +++ b/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java @@ -163,18 +163,45 @@ public final class ChunkUtils { */ @NotNull public static long[] getChunksInRange(@NotNull Position position, int range) { - range = range * 2; - long[] visibleChunks = new long[MathUtils.square(range + 1)]; - final int startLoop = -(range / 2); - final int endLoop = range / 2 + 1; - int counter = 0; - for (int x = startLoop; x < endLoop; x++) { - for (int z = startLoop; z < endLoop; z++) { - final int chunkX = getChunkCoordinate(position.getX() + Chunk.CHUNK_SIZE_X * x); - final int chunkZ = getChunkCoordinate(position.getZ() + Chunk.CHUNK_SIZE_Z * z); - visibleChunks[counter++] = getChunkIndex(chunkX, chunkZ); + long[] visibleChunks = new long[MathUtils.square(range * 2 + 1)]; + + int xDistance = 0; + int xDirection = 1; + int zDistance = 0; + int zDirection = -1; + int len = 1; + int corner = 0; + + for (int i = 0; i < visibleChunks.length; i++) { + final int chunkX = getChunkCoordinate(xDistance * Chunk.CHUNK_SIZE_X + position.getX()); + final int chunkZ = getChunkCoordinate(zDistance * Chunk.CHUNK_SIZE_Z + position.getZ()); + visibleChunks[i] = getChunkIndex(chunkX, chunkZ); + + if (corner % 2 == 0) { + // step on X axis + xDistance += xDirection; + + if (Math.abs(xDistance) == len) { + // hit corner + corner++; + xDirection = -xDirection; + } + } else { + // step on Z axis + zDistance += zDirection; + + if (Math.abs(zDistance) == len) { + // hit corner + corner++; + zDirection = -zDirection; + + if (corner % 4 == 0) { + len++; + } + } } } + return visibleChunks; } From bea622db0bf0cc91a734ece012198f07978b8228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9meth=20Noel?= Date: Wed, 5 May 2021 17:48:57 +0200 Subject: [PATCH 03/11] Only load spawning chunk before spawning the player --- .../java/net/minestom/server/entity/Player.java | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 4dd164353..277290249 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -606,19 +606,12 @@ public class Player extends LivingEntity implements CommandSender, Localizable, sendDimension(instanceDimensionType); } - // Load all the required chunks - final long[] visibleChunks = ChunkUtils.getChunksInRange(spawnPosition, getChunkRange()); + // Only load the spawning chunk to speed up login, remaining chunks are loaded in #spawnPlayer + final long[] visibleChunks = ChunkUtils.getChunksInRange(spawnPosition, 0); - final ChunkCallback endCallback = chunk -> { - // This is the last chunk to be loaded , spawn player - spawnPlayer(instance, spawnPosition, firstSpawn, true, dimensionChange); - }; - - // Chunk 0;0 always needs to be loaded - instance.loadChunk(0, 0, chunk -> - // Load all the required chunks - ChunkUtils.optionalLoadAll(instance, visibleChunks, null, endCallback)); + final ChunkCallback endCallback = chunk -> spawnPlayer(instance, spawnPosition, firstSpawn, true, dimensionChange); + ChunkUtils.optionalLoadAll(instance, visibleChunks, null, endCallback); } else { // The player already has the good version of all the chunks. // We just need to refresh his entity viewing list and add him to the instance From 7881cea36c1c1ca1fae7766c6ffccd7b5a8ee89f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9meth=20Noel?= Date: Wed, 5 May 2021 18:11:24 +0200 Subject: [PATCH 04/11] Wait for client settings --- .../java/net/minestom/server/entity/Player.java | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 277290249..ac2716467 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -2346,17 +2346,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, * based on which one is the lowest */ public int getChunkRange() { - final int playerRange = getSettings().viewDistance; - if (playerRange < 1) { - // Didn't receive settings packet yet (is the case on login) - // In this case we send an arbitrary number of chunks - // Will be updated in PlayerSettings#refresh. - // Non-compliant clients might also be stuck with this view - return 7; - } else { - final int serverRange = MinecraftServer.getChunkViewDistance(); - return Math.min(playerRange, serverRange); - } + return Math.min(getSettings().viewDistance, MinecraftServer.getChunkViewDistance()); } /** @@ -2588,6 +2578,10 @@ public class Player extends LivingEntity implements CommandSender, Localizable, private byte displayedSkinParts; private MainHand mainHand; + public PlayerSettings() { + viewDistance = -1; + } + /** * The player game language. * From 3eb4981990dc6aeb9d7d8c83eb57b9e93e1661a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9meth=20Noel?= Date: Wed, 5 May 2021 18:25:20 +0200 Subject: [PATCH 05/11] Removed redundant initial value --- src/main/java/net/minestom/server/entity/Player.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index ac2716467..92e0dfec4 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -2578,10 +2578,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable, private byte displayedSkinParts; private MainHand mainHand; - public PlayerSettings() { - viewDistance = -1; - } - /** * The player game language. * From d360904f77b24baf35f470db2d00d04f971d8174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9meth=20Noel?= Date: Wed, 5 May 2021 23:55:01 +0200 Subject: [PATCH 06/11] Fix different spawn pos check --- src/main/java/net/minestom/server/entity/Player.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 5fc75ae29..536aa0199 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -612,13 +612,13 @@ public class Player extends LivingEntity implements CommandSender, Localizable, // Only load the spawning chunk to speed up login, remaining chunks are loaded in #spawnPlayer final long[] visibleChunks = ChunkUtils.getChunksInRange(spawnPosition, 0); - final ChunkCallback endCallback = chunk -> spawnPlayer(instance, spawnPosition, firstSpawn, true, dimensionChange); + final ChunkCallback endCallback = chunk -> spawnPlayer(instance, spawnPosition, firstSpawn, dimensionChange); ChunkUtils.optionalLoadAll(instance, visibleChunks, null, endCallback); } else { // The player already has the good version of all the chunks. // We just need to refresh his entity viewing list and add him to the instance - spawnPlayer(instance, spawnPosition, false, false, false); + spawnPlayer(instance, spawnPosition, false, false); } } @@ -645,21 +645,19 @@ public class Player extends LivingEntity implements CommandSender, Localizable, * @param firstSpawn true if this is the player first spawn */ private void spawnPlayer(@NotNull Instance instance, @NotNull Position spawnPosition, - boolean firstSpawn, boolean updateChunks, boolean dimensionChange) { + boolean firstSpawn, boolean dimensionChange) { // Clear previous instance elements if (!firstSpawn) { this.viewableChunks.forEach(chunk -> chunk.removeViewer(this)); this.viewableEntities.forEach(entity -> entity.removeViewer(this)); } + final boolean differentSpawnPos = !position.isSimilar(spawnPosition); super.setInstance(instance, spawnPosition); - if (!position.isSimilar(spawnPosition) && !firstSpawn) { + if (differentSpawnPos && !firstSpawn) { // Player changed instance at a different position teleport(spawnPosition); - } else if (updateChunks) { - // Send newly visible chunks to player once spawned in the instance - refreshVisibleChunks(); } if (dimensionChange || firstSpawn) { From de7241ef6cf43a7f610f0cbdae8ffbd9354424f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9meth=20Noel?= Date: Fri, 7 May 2021 00:54:38 +0200 Subject: [PATCH 07/11] Re-enable chunk unloading, corrected packet order --- src/main/java/net/minestom/server/entity/Player.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 536aa0199..873e540e6 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -1547,27 +1547,25 @@ public class Player extends LivingEntity implements CommandSender, Localizable, final int[] oldChunks = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunks, updatedVisibleChunks); final int[] newChunks = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunks, lastVisibleChunks); + // Update client render distance + updateViewPosition(newChunk.getChunkX(), newChunk.getChunkZ()); + // Unload old chunks for (int index : oldChunks) { final long chunkIndex = lastVisibleChunks[index]; final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex); final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex); - // TODO prevent the client from getting lag spikes when re-loading large chunks - // Probably by having a distinction between visible and loaded (cache) chunks - /*UnloadChunkPacket unloadChunkPacket = new UnloadChunkPacket(); + final UnloadChunkPacket unloadChunkPacket = new UnloadChunkPacket(); unloadChunkPacket.chunkX = chunkX; unloadChunkPacket.chunkZ = chunkZ; - playerConnection.sendPacket(unloadChunkPacket);*/ + playerConnection.sendPacket(unloadChunkPacket); final Chunk chunk = instance.getChunk(chunkX, chunkZ); if (chunk != null) chunk.removeViewer(this); } - // Update client render distance - updateViewPosition(newChunk.getChunkX(), newChunk.getChunkZ()); - // Load new chunks for (int index : newChunks) { final long chunkIndex = updatedVisibleChunks[index]; From b75a034b2de523c0de6a7c324c9d4abf436e4730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9meth=20Noel?= Date: Fri, 7 May 2021 02:24:28 +0200 Subject: [PATCH 08/11] Reintroduced two phase chunk sending --- src/main/java/net/minestom/server/entity/Player.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 873e540e6..71ac7cc4b 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -655,6 +655,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable, super.setInstance(instance, spawnPosition); + refreshVisibleChunks(); + if (differentSpawnPos && !firstSpawn) { // Player changed instance at a different position teleport(spawnPosition); @@ -2593,6 +2595,10 @@ public class Player extends LivingEntity implements CommandSender, Localizable, private byte displayedSkinParts; private MainHand mainHand; + public PlayerSettings() { + viewDistance = 2; + } + /** * The player game language. * From 7deeb6608907bbcbe8358ca69fbacceaeaf4801b Mon Sep 17 00:00:00 2001 From: TheMode Date: Mon, 10 May 2021 00:51:35 +0200 Subject: [PATCH 09/11] Reintroduce updateChunks boolean --- .../net/minestom/server/entity/Player.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 71ac7cc4b..7e773a230 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -69,7 +69,6 @@ import net.minestom.server.utils.entity.EntityUtils; import net.minestom.server.utils.identity.NamedAndIdentified; import net.minestom.server.utils.instance.InstanceUtils; import net.minestom.server.utils.inventory.PlayerInventoryUtils; -import net.minestom.server.utils.player.PlayerUtils; import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.utils.time.UpdateOption; @@ -612,13 +611,14 @@ public class Player extends LivingEntity implements CommandSender, Localizable, // Only load the spawning chunk to speed up login, remaining chunks are loaded in #spawnPlayer final long[] visibleChunks = ChunkUtils.getChunksInRange(spawnPosition, 0); - final ChunkCallback endCallback = chunk -> spawnPlayer(instance, spawnPosition, firstSpawn, dimensionChange); + final ChunkCallback endCallback = + chunk -> spawnPlayer(instance, spawnPosition, firstSpawn, dimensionChange, true); ChunkUtils.optionalLoadAll(instance, visibleChunks, null, endCallback); } else { // The player already has the good version of all the chunks. // We just need to refresh his entity viewing list and add him to the instance - spawnPlayer(instance, spawnPosition, false, false); + spawnPlayer(instance, spawnPosition, false, false, false); } } @@ -643,21 +643,22 @@ public class Player extends LivingEntity implements CommandSender, Localizable, * * @param spawnPosition the position to teleport the player * @param firstSpawn true if this is the player first spawn + * @param updateChunks true if chunks should be refreshed, false if the new instance shares the same + * chunks */ private void spawnPlayer(@NotNull Instance instance, @NotNull Position spawnPosition, - boolean firstSpawn, boolean dimensionChange) { - // Clear previous instance elements + boolean firstSpawn, boolean dimensionChange, boolean updateChunks) { if (!firstSpawn) { + // Player instance changed, clear current viewable collections this.viewableChunks.forEach(chunk -> chunk.removeViewer(this)); this.viewableEntities.forEach(entity -> entity.removeViewer(this)); } - final boolean differentSpawnPos = !position.isSimilar(spawnPosition); super.setInstance(instance, spawnPosition); - refreshVisibleChunks(); - - if (differentSpawnPos && !firstSpawn) { + if (updateChunks) { + refreshVisibleChunks(); + } else if (!position.isSimilar(spawnPosition)) { // Player changed instance at a different position teleport(spawnPosition); } From db8219d5eee4e8445403962d0f23c50ea2f9badf Mon Sep 17 00:00:00 2001 From: TheMode Date: Mon, 10 May 2021 01:08:51 +0200 Subject: [PATCH 10/11] Remove unnecessary condition --- src/main/java/net/minestom/server/entity/Player.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 7e773a230..8a7ecad0c 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -658,9 +658,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable, if (updateChunks) { refreshVisibleChunks(); - } else if (!position.isSimilar(spawnPosition)) { - // Player changed instance at a different position - teleport(spawnPosition); } if (dimensionChange || firstSpawn) { From 9cf897a4cc542aaba7f9abf6cf6071900cb7d2a1 Mon Sep 17 00:00:00 2001 From: TheMode Date: Mon, 10 May 2021 01:17:43 +0200 Subject: [PATCH 11/11] Remove empty lines --- .../java/net/minestom/server/utils/chunk/ChunkUtils.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java b/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java index 687df6036..22be4d414 100644 --- a/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java +++ b/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java @@ -161,10 +161,8 @@ public final class ChunkUtils { * @param range how far should it retrieves chunk * @return an array containing chunks index */ - @NotNull - public static long[] getChunksInRange(@NotNull Position position, int range) { + public static @NotNull long[] getChunksInRange(@NotNull Position position, int range) { long[] visibleChunks = new long[MathUtils.square(range * 2 + 1)]; - int xDistance = 0; int xDirection = 1; int zDistance = 0; @@ -201,7 +199,6 @@ public final class ChunkUtils { } } } - return visibleChunks; }