Merge pull request #274 from Kebab11noel/position-cleanup

Chunk loading rework
This commit is contained in:
TheMode 2021-05-10 01:18:26 +02:00 committed by GitHub
commit d8274bbdf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 51 deletions

View File

@ -683,8 +683,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
* <i>(performed using {@link #synchronizePosition()})</i></li>
* <li>{@link EntityPositionAndRotationPacket} if {@code positionChange && viewChange}</li>
* <li>{@link EntityPositionPacket} if {@code positionChange}</li>
* <li>{@link EntityRotationPacket} if {@code viewChange}
* <i>(performed using {@link #setView(float, float)})</i></li>
* <li>{@link EntityRotationPacket} and {@link EntityHeadLookPacket} if {@code viewChange}</li>
* </ol>
* In case of a player's position and/or view change an additional {@link PlayerPositionAndLookPacket}
* is sent to self.

View File

@ -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;
@ -609,19 +608,13 @@ 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, 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
@ -650,22 +643,20 @@ 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 updateChunks, 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));
}
super.setInstance(instance, spawnPosition);
if (!position.isSimilar(spawnPosition) && !firstSpawn) {
// Player changed instance at a different position
teleport(spawnPosition);
} else if (updateChunks) {
// Send newly visible chunks to player once spawned in the instance
if (updateChunks) {
refreshVisibleChunks();
}
@ -1556,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];
@ -2372,17 +2361,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());
}
/**
@ -2614,6 +2593,10 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
private byte displayedSkinParts;
private MainHand mainHand;
public PlayerSettings() {
viewDistance = 2;
}
/**
* The player game language.
*

View File

@ -161,18 +161,42 @@ 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) {
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);
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;
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;