Optimize chunk index to coordinate conversion + made the chunk map in InstanceContainer using long primitive but non-concurrent, requiring synchronization

This commit is contained in:
themode 2020-08-17 16:50:23 +02:00
parent 97a1141583
commit e5e1d1614b
7 changed files with 241 additions and 202 deletions

View File

@ -63,14 +63,15 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
protected float cacheX, cacheY, cacheZ; // Used to synchronize with #getPosition protected float cacheX, cacheY, cacheZ; // Used to synchronize with #getPosition
protected float lastYaw, lastPitch; protected float lastYaw, lastPitch;
protected float cacheYaw, cachePitch; protected float cacheYaw, cachePitch;
// Synchronization
private static final long SYNCHRONIZATION_DELAY = 1500; // In ms
private BoundingBox boundingBox; private BoundingBox boundingBox;
protected Entity vehicle; protected Entity vehicle;
// Velocity // Velocity
protected Vector velocity = new Vector(); // Movement in block per second protected Vector velocity = new Vector(); // Movement in block per second
protected long lastVelocityUpdateTime; // Reset velocity to 0 after countdown
protected float gravityDragPerTick; protected float gravityDragPerTick;
protected float eyeHeight; protected float eyeHeight;
@ -84,13 +85,17 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
private boolean removed; private boolean removed;
private boolean shouldRemove; private boolean shouldRemove;
private long scheduledRemoveTime; private long scheduledRemoveTime;
private final Set<Entity> passengers = new CopyOnWriteArraySet<>(); private final Set<Entity> passengers = new CopyOnWriteArraySet<>();
private long lastUpdate; private long lastUpdate;
private final EntityType entityType; private final EntityType entityType;
protected long lastVelocityUpdateTime; // Reset velocity to 0 after countdown
private final Map<Class<? extends Event>, List<EventCallback>> eventCallbacks = new ConcurrentHashMap<>(); // Synchronization
private static final long SYNCHRONIZATION_DELAY = 1500; // In ms
private long lastSynchronizationTime; private long lastSynchronizationTime;
private final Map<Class<? extends Event>, List<EventCallback>> eventCallbacks = new ConcurrentHashMap<>();
// Metadata // Metadata
protected boolean onFire; protected boolean onFire;
protected boolean crouched; protected boolean crouched;
@ -990,8 +995,10 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
final int[] oldChunksEntity = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunksEntity, updatedVisibleChunksEntity); final int[] oldChunksEntity = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunksEntity, updatedVisibleChunksEntity);
for (int index : oldChunksEntity) { for (int index : oldChunksEntity) {
final int[] chunkPos = ChunkUtils.getChunkCoord(lastVisibleChunksEntity[index]); final long chunkIndex = lastVisibleChunksEntity[index];
final Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]); final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex);
final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex);
final Chunk chunk = instance.getChunk(chunkX, chunkZ);
if (chunk == null) if (chunk == null)
continue; continue;
instance.getChunkEntities(chunk).forEach(ent -> { instance.getChunkEntities(chunk).forEach(ent -> {
@ -1010,8 +1017,10 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
final int[] newChunksEntity = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunksEntity, lastVisibleChunksEntity); final int[] newChunksEntity = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunksEntity, lastVisibleChunksEntity);
for (int index : newChunksEntity) { for (int index : newChunksEntity) {
final int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunksEntity[index]); final long chunkIndex = updatedVisibleChunksEntity[index];
final Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]); final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex);
final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex);
final Chunk chunk = instance.getChunk(chunkX, chunkZ);
if (chunk == null) if (chunk == null)
continue; continue;
instance.getChunkEntities(chunk).forEach(ent -> { instance.getChunkEntities(chunk).forEach(ent -> {

View File

@ -551,9 +551,9 @@ public class Player extends LivingEntity implements CommandSender {
AtomicInteger counter = new AtomicInteger(0); AtomicInteger counter = new AtomicInteger(0);
for (long visibleChunk : visibleChunks) { for (long visibleChunk : visibleChunks) {
final int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunk); final int chunkX = ChunkUtils.getChunkCoordX(visibleChunk);
final int chunkX = chunkPos[0]; final int chunkZ = ChunkUtils.getChunkCoordZ(visibleChunk);
final int chunkZ = chunkPos[1];
Consumer<Chunk> callback = (chunk) -> { Consumer<Chunk> callback = (chunk) -> {
if (chunk != null) { if (chunk != null) {
chunk.addViewer(this); chunk.addViewer(this);
@ -1200,13 +1200,16 @@ public class Player extends LivingEntity implements CommandSender {
// Unload old chunks // Unload old chunks
for (int index : oldChunks) { for (int index : oldChunks) {
final int[] chunkPos = ChunkUtils.getChunkCoord(lastVisibleChunks[index]); final long chunkIndex = lastVisibleChunks[index];
final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex);
final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex);
UnloadChunkPacket unloadChunkPacket = new UnloadChunkPacket(); UnloadChunkPacket unloadChunkPacket = new UnloadChunkPacket();
unloadChunkPacket.chunkX = chunkPos[0]; unloadChunkPacket.chunkX = chunkX;
unloadChunkPacket.chunkZ = chunkPos[1]; unloadChunkPacket.chunkZ = chunkZ;
playerConnection.sendPacket(unloadChunkPacket); playerConnection.sendPacket(unloadChunkPacket);
final Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]); final Chunk chunk = instance.getChunk(chunkX, chunkZ);
if (chunk != null) if (chunk != null)
chunk.removeViewer(this); chunk.removeViewer(this);
} }
@ -1215,8 +1218,11 @@ public class Player extends LivingEntity implements CommandSender {
// Load new chunks // Load new chunks
for (int index : newChunks) { for (int index : newChunks) {
final int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunks[index]); final long chunkIndex = updatedVisibleChunks[index];
instance.loadOptionalChunk(chunkPos[0], chunkPos[1], chunk -> { final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex);
final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex);
instance.loadOptionalChunk(chunkX, chunkZ, chunk -> {
if (chunk == null) { if (chunk == null) {
// Cannot load chunk (auto load is not enabled) // Cannot load chunk (auto load is not enabled)
return; return;

View File

@ -832,7 +832,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
} }
private Set<Entity> getEntitiesInChunk(long index) { private Set<Entity> getEntitiesInChunk(long index) {
final Set<Entity> entities = chunkEntities.getOrDefault(index, new CopyOnWriteArraySet<>()); final Set<Entity> entities = chunkEntities.computeIfAbsent(index, i -> new CopyOnWriteArraySet<>());
return entities; return entities;
} }

View File

@ -1,5 +1,7 @@
package net.minestom.server.instance; package net.minestom.server.instance;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import lombok.Setter; import lombok.Setter;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.data.Data; import net.minestom.server.data.Data;
@ -30,7 +32,6 @@ import net.minestom.server.world.DimensionType;
import net.minestom.server.world.biomes.Biome; import net.minestom.server.world.biomes.Biome;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
@ -52,7 +53,8 @@ public class InstanceContainer extends Instance {
private List<SharedInstance> sharedInstances = new CopyOnWriteArrayList<>(); private List<SharedInstance> sharedInstances = new CopyOnWriteArrayList<>();
private ChunkGenerator chunkGenerator; private ChunkGenerator chunkGenerator;
private Map<Long, Chunk> chunks = new ConcurrentHashMap<>(); // WARNING: need to be synchronized properly
private Long2ObjectMap<Chunk> chunks = new Long2ObjectOpenHashMap();
private Set<Chunk> scheduledChunksToRemove = new HashSet<>(); private Set<Chunk> scheduledChunksToRemove = new HashSet<>();
private ReadWriteLock changingBlockLock = new ReentrantReadWriteLock(); private ReadWriteLock changingBlockLock = new ReentrantReadWriteLock();
@ -333,7 +335,8 @@ public class InstanceContainer extends Instance {
@Override @Override
public Chunk getChunk(int chunkX, int chunkZ) { public Chunk getChunk(int chunkX, int chunkZ) {
final Chunk chunk = chunks.get(ChunkUtils.getChunkIndex(chunkX, chunkZ)); final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
final Chunk chunk = chunks.get(index);
return ChunkUtils.isLoaded(chunk) ? chunk : null; return ChunkUtils.isLoaded(chunk) ? chunk : null;
} }
@ -390,11 +393,13 @@ public class InstanceContainer extends Instance {
e.printStackTrace(); e.printStackTrace();
} }
} else { } else {
final Iterator<Chunk> chunks = getChunks().iterator(); synchronized (chunks) {
while (chunks.hasNext()) { final Iterator<Chunk> chunkIterator = chunks.values().iterator();
final Chunk chunk = chunks.next(); while (chunkIterator.hasNext()) {
final boolean isLast = !chunks.hasNext(); final Chunk chunk = chunkIterator.next();
saveChunkToStorageFolder(chunk, isLast ? callback : null); final boolean isLast = !chunkIterator.hasNext();
saveChunkToStorageFolder(chunk, isLast ? callback : null);
}
} }
} }
} }
@ -503,6 +508,11 @@ public class InstanceContainer extends Instance {
this.chunkGenerator = chunkGenerator; this.chunkGenerator = chunkGenerator;
} }
/**
* Get all the instance chunks
*
* @return the chunks of this instance
*/
public Collection<Chunk> getChunks() { public Collection<Chunk> getChunks() {
return Collections.unmodifiableCollection(chunks.values()); return Collections.unmodifiableCollection(chunks.values());
} }

View File

@ -94,8 +94,10 @@ public abstract class ThreadProvider {
* @param time the time of the update in milliseconds * @param time the time of the update in milliseconds
*/ */
protected void processChunkTick(Instance instance, long chunkIndex, long time) { protected void processChunkTick(Instance instance, long chunkIndex, long time) {
final int[] chunkCoordinates = ChunkUtils.getChunkCoord(chunkIndex); final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex);
final Chunk chunk = instance.getChunk(chunkCoordinates[0], chunkCoordinates[1]); final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex);
final Chunk chunk = instance.getChunk(chunkX, chunkZ);
if (!ChunkUtils.isLoaded(chunk)) if (!ChunkUtils.isLoaded(chunk))
return; return;

View File

@ -10,199 +10,211 @@ import net.minestom.server.utils.Position;
public final class ChunkUtils { public final class ChunkUtils {
private ChunkUtils() { private ChunkUtils() {
} }
/** /**
* Get if a chunk is loaded * Get if a chunk is loaded
* *
* @param chunk the chunk to check * @param chunk the chunk to check
* @return true if the chunk is loaded, false otherwise * @return true if the chunk is loaded, false otherwise
*/ */
public static boolean isLoaded(Chunk chunk) { public static boolean isLoaded(Chunk chunk) {
return chunk != null && chunk.isLoaded(); return chunk != null && chunk.isLoaded();
} }
/** /**
* Get if a chunk is loaded * Get if a chunk is loaded
* *
* @param instance the instance to check * @param instance the instance to check
* @param x instance X coordinate * @param x instance X coordinate
* @param z instance Z coordinate * @param z instance Z coordinate
* @return true if the chunk is loaded, false otherwise * @return true if the chunk is loaded, false otherwise
*/ */
public static boolean isLoaded(Instance instance, float x, float z) { public static boolean isLoaded(Instance instance, float x, float z) {
final int chunkX = getChunkCoordinate((int) x); final int chunkX = getChunkCoordinate((int) x);
final int chunkZ = getChunkCoordinate((int) z); final int chunkZ = getChunkCoordinate((int) z);
final Chunk chunk = instance.getChunk(chunkX, chunkZ); final Chunk chunk = instance.getChunk(chunkX, chunkZ);
return isLoaded(chunk); return isLoaded(chunk);
} }
/** /**
* @param xz the instance coordinate to convert * @param xz the instance coordinate to convert
* @return the chunk X or Z based on the argument * @return the chunk X or Z based on the argument
*/ */
public static int getChunkCoordinate(int xz) { public static int getChunkCoordinate(int xz) {
// Assume Chunk.CHUNK_SIZE_X == Chunk.CHUNK_SIZE_Z // Assume Chunk.CHUNK_SIZE_X == Chunk.CHUNK_SIZE_Z
return Math.floorDiv(xz, Chunk.CHUNK_SIZE_X); return Math.floorDiv(xz, Chunk.CHUNK_SIZE_X);
} }
/** /**
* Get the chunk index of chunk coordinates * Get the chunk index of chunk coordinates
* *
* @param chunkX the chunk X * @param chunkX the chunk X
* @param chunkZ the chunk Z * @param chunkZ the chunk Z
* @return a number storing the chunk X and Z * @return a number storing the chunk X and Z
*/ */
public static long getChunkIndex(int chunkX, int chunkZ) { public static long getChunkIndex(int chunkX, int chunkZ) {
return (((long) chunkX) << 32) | (chunkZ & 0xffffffffL); return (((long) chunkX) << 32) | (chunkZ & 0xffffffffL);
} }
public static long getChunkIndexWithSection(int chunkX, int chunkZ, int section) { public static long getChunkIndexWithSection(int chunkX, int chunkZ, int section) {
long l = 0L; long l = 0L;
l |= ((long) chunkX & 4194303L) << 42; l |= ((long) chunkX & 4194303L) << 42;
l |= ((long) section & 1048575L); l |= ((long) section & 1048575L);
l |= ((long) chunkZ & 4194303L) << 20; l |= ((long) chunkZ & 4194303L) << 20;
return l; return l;
} }
/** /**
* @param index the chunk index computed by {@link #getChunkIndex(int, int)} * Convert a chunk index to its chunk X position
* @return an array containing both the chunk X and Z (index 0 = X; index 1 = Z) *
*/ * @param index the chunk index computed by {@link #getChunkIndex(int, int)}
public static int[] getChunkCoord(long index) { * @return the chunk X based on the index
final int chunkX = (int) (index >> 32); */
final int chunkZ = (int) index; public static int getChunkCoordX(long index) {
return new int[]{chunkX, chunkZ}; return (int) (index >> 32);
} }
public static short getLocalBlockPosAsShort(int x, int y, int z) { /**
x = x % 16; * Convert a chunk index to its chunk Z position
z = z % 16; *
return (short) (x << 8 | y << 4 | z); * @param index the chunk index computed by {@link #getChunkIndex(int, int)}
} * @return the chunk Z based on the index
*/
public static int getChunkCoordZ(long index) {
final int chunkX = (int) (index >> 32);
final int chunkZ = (int) index;
return (int) index;
}
public static int getSectionAt(int y) { public static short getLocalBlockPosAsShort(int x, int y, int z) {
return y / Chunk.CHUNK_SECTION_SIZE; x = x % 16;
} z = z % 16;
return (short) (x << 8 | y << 4 | z);
}
/** public static int getSectionAt(int y) {
* Get the chunks in range of a position return y / Chunk.CHUNK_SECTION_SIZE;
* }
* @param position the initial position
* @param range how far should it retrieves chunk
* @return an array containing chunks index which can be converted using {@link #getChunkCoord(long)}
*/
public static long[] getChunksInRange(final 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((int) (position.getX() + Chunk.CHUNK_SIZE_X * x));
final int chunkZ = getChunkCoordinate((int) (position.getZ() + Chunk.CHUNK_SIZE_Z * z));
visibleChunks[counter] = getChunkIndex(chunkX, chunkZ);
counter++;
}
}
return visibleChunks;
}
/** /**
* Get all the loaded neighbours of a chunk and itself, no diagonals * Get the chunks in range of a position
* *
* @param instance the instance of the chunks * @param position the initial position
* @param chunkX the chunk X * @param range how far should it retrieves chunk
* @param chunkZ the chunk Z * @return an array containing chunks index
* @return an array containing all the loaded neighbours */
* can be deserialized using {@link #indexToChunkPosition(int)} public static long[] getChunksInRange(final Position position, int range) {
*/ range = range * 2;
public static long[] getNeighbours(Instance instance, int chunkX, int chunkZ) { long[] visibleChunks = new long[MathUtils.square(range + 1)];
LongList chunks = new LongArrayList(); final int startLoop = -(range / 2);
// Constants used to loop through the neighbors final int endLoop = range / 2 + 1;
final int[] posX = {1, 0, -1}; int counter = 0;
final int[] posZ = {1, 0, -1}; for (int x = startLoop; x < endLoop; x++) {
for (int z = startLoop; z < endLoop; z++) {
final int chunkX = getChunkCoordinate((int) (position.getX() + Chunk.CHUNK_SIZE_X * x));
final int chunkZ = getChunkCoordinate((int) (position.getZ() + Chunk.CHUNK_SIZE_Z * z));
visibleChunks[counter] = getChunkIndex(chunkX, chunkZ);
counter++;
}
}
return visibleChunks;
}
for (int x : posX) { /**
for (int z : posZ) { * Get all the loaded neighbours of a chunk and itself, no diagonals
*
* @param instance the instance of the chunks
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @return an array containing all the loaded neighbours
* can be deserialized using {@link #indexToChunkPosition(int)}
*/
public static long[] getNeighbours(Instance instance, int chunkX, int chunkZ) {
LongList chunks = new LongArrayList();
// Constants used to loop through the neighbors
final int[] posX = {1, 0, -1};
final int[] posZ = {1, 0, -1};
// No diagonal check for (int x : posX) {
if ((Math.abs(x) + Math.abs(z)) == 2) for (int z : posZ) {
continue;
final int targetX = chunkX + x; // No diagonal check
final int targetZ = chunkZ + z; if ((Math.abs(x) + Math.abs(z)) == 2)
final Chunk chunk = instance.getChunk(targetX, targetZ); continue;
if (ChunkUtils.isLoaded(chunk)) {
// Chunk is loaded, add it
final long index = getChunkIndex(targetX, targetZ);
chunks.add(index);
}
} final int targetX = chunkX + x;
} final int targetZ = chunkZ + z;
return chunks.toArray(new long[0]); final Chunk chunk = instance.getChunk(targetX, targetZ);
} if (ChunkUtils.isLoaded(chunk)) {
// Chunk is loaded, add it
final long index = getChunkIndex(targetX, targetZ);
chunks.add(index);
}
/** }
* Get the block index of a position }
* return chunks.toArray(new long[0]);
* @param x the block X }
* @param y the block Y
* @param z the block Z
* @return an index which can be used to store and retrieve later data linked to a block position
*/
public static int getBlockIndex(int x, int y, int z) {
x = x % Chunk.CHUNK_SIZE_X;
z = z % Chunk.CHUNK_SIZE_Z;
short index = (short) (x & 0x000F); /**
index |= (y << 4) & 0x0FF0; * Get the block index of a position
index |= (z << 12) & 0xF000; *
return index & 0xffff; * @param x the block X
} * @param y the block Y
* @param z the block Z
* @return an index which can be used to store and retrieve later data linked to a block position
*/
public static int getBlockIndex(int x, int y, int z) {
x = x % Chunk.CHUNK_SIZE_X;
z = z % Chunk.CHUNK_SIZE_Z;
/** short index = (short) (x & 0x000F);
* @param index an index computed from {@link #getBlockIndex(int, int, int)} index |= (y << 4) & 0x0FF0;
* @param chunkX the chunk X index |= (z << 12) & 0xF000;
* @param chunkZ the chunk Z return index & 0xffff;
* @return the instance position of the block located in {@code index} }
*/
public static BlockPosition getBlockPosition(int index, int chunkX, int chunkZ) {
final int[] pos = indexToPosition(index, chunkX, chunkZ);
return new BlockPosition(pos[0], pos[1], pos[2]);
}
/** /**
* @param index an index computed from {@link #getBlockIndex(int, int, int)} * @param index an index computed from {@link #getBlockIndex(int, int, int)}
* @param chunkX the chunk X * @param chunkX the chunk X
* @param chunkZ the chunk Z * @param chunkZ the chunk Z
* @return the world position of the specified index with its chunks being {@code chunkX} and {@code chunk Z} * @return the instance position of the block located in {@code index}
* positions in the array are in the order X/Y/Z */
*/ public static BlockPosition getBlockPosition(int index, int chunkX, int chunkZ) {
public static int[] indexToPosition(int index, int chunkX, int chunkZ) { final int[] pos = indexToPosition(index, chunkX, chunkZ);
int z = (byte) (index >> 12 & 0xF); return new BlockPosition(pos[0], pos[1], pos[2]);
final int y = (index >>> 4 & 0xFF); }
// index >> 0 = index
int x = (byte) (index & 0xF);
x += 16 * chunkX; /**
z += 16 * chunkZ; * @param index an index computed from {@link #getBlockIndex(int, int, int)}
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @return the world position of the specified index with its chunks being {@code chunkX} and {@code chunk Z}
* positions in the array are in the order X/Y/Z
*/
public static int[] indexToPosition(int index, int chunkX, int chunkZ) {
int z = (byte) (index >> 12 & 0xF);
final int y = (index >>> 4 & 0xFF);
// index >> 0 = index
int x = (byte) (index & 0xF);
return new int[]{x, y, z}; x += 16 * chunkX;
} z += 16 * chunkZ;
/** return new int[]{x, y, z};
* @param index an index computed from {@link #getBlockIndex(int, int, int)} }
* @return the chunk position (O-15) of the specified index,
* positions in the array are in the order X/Y/Z /**
*/ * @param index an index computed from {@link #getBlockIndex(int, int, int)}
public static int[] indexToChunkPosition(int index) { * @return the chunk position (O-15) of the specified index,
return indexToPosition(index, 0, 0); * positions in the array are in the order X/Y/Z
} */
public static int[] indexToChunkPosition(int index) {
return indexToPosition(index, 0, 0);
}
} }

View File

@ -25,9 +25,9 @@ public final class EntityUtils {
long[] visibleChunksEntity = ChunkUtils.getChunksInRange(ent2.getPosition(), MinecraftServer.ENTITY_VIEW_DISTANCE); long[] visibleChunksEntity = ChunkUtils.getChunksInRange(ent2.getPosition(), MinecraftServer.ENTITY_VIEW_DISTANCE);
for (long visibleChunk : visibleChunksEntity) { for (long visibleChunk : visibleChunksEntity) {
final int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunk); final int chunkX = ChunkUtils.getChunkCoordX(visibleChunk);
final int chunkX = chunkPos[0]; final int chunkZ = ChunkUtils.getChunkCoordZ(visibleChunk);
final int chunkZ = chunkPos[1];
if (chunk.getChunkX() == chunkX && chunk.getChunkZ() == chunkZ) if (chunk.getChunkX() == chunkX && chunk.getChunkZ() == chunkZ)
return true; return true;
} }