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

View File

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

View File

@ -832,7 +832,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
}
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;
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.instance;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import lombok.Setter;
import net.minestom.server.MinecraftServer;
import net.minestom.server.data.Data;
@ -30,7 +32,6 @@ import net.minestom.server.world.DimensionType;
import net.minestom.server.world.biomes.Biome;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.Lock;
@ -52,7 +53,8 @@ public class InstanceContainer extends Instance {
private List<SharedInstance> sharedInstances = new CopyOnWriteArrayList<>();
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 ReadWriteLock changingBlockLock = new ReentrantReadWriteLock();
@ -333,7 +335,8 @@ public class InstanceContainer extends Instance {
@Override
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;
}
@ -390,14 +393,16 @@ public class InstanceContainer extends Instance {
e.printStackTrace();
}
} else {
final Iterator<Chunk> chunks = getChunks().iterator();
while (chunks.hasNext()) {
final Chunk chunk = chunks.next();
final boolean isLast = !chunks.hasNext();
synchronized (chunks) {
final Iterator<Chunk> chunkIterator = chunks.values().iterator();
while (chunkIterator.hasNext()) {
final Chunk chunk = chunkIterator.next();
final boolean isLast = !chunkIterator.hasNext();
saveChunkToStorageFolder(chunk, isLast ? callback : null);
}
}
}
}
@Override
public BlockBatch createBlockBatch() {
@ -503,6 +508,11 @@ public class InstanceContainer extends Instance {
this.chunkGenerator = chunkGenerator;
}
/**
* Get all the instance chunks
*
* @return the chunks of this instance
*/
public Collection<Chunk> getChunks() {
return Collections.unmodifiableCollection(chunks.values());
}

View File

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

View File

@ -69,13 +69,25 @@ public final class ChunkUtils {
}
/**
* Convert a chunk index to its chunk X position
*
* @param index the chunk index computed by {@link #getChunkIndex(int, int)}
* @return an array containing both the chunk X and Z (index 0 = X; index 1 = Z)
* @return the chunk X based on the index
*/
public static int[] getChunkCoord(long index) {
public static int getChunkCoordX(long index) {
return (int) (index >> 32);
}
/**
* Convert a chunk index to its chunk Z position
*
* @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 new int[]{chunkX, chunkZ};
return (int) index;
}
public static short getLocalBlockPosAsShort(int x, int y, int z) {
@ -93,7 +105,7 @@ public final class ChunkUtils {
*
* @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)}
* @return an array containing chunks index
*/
public static long[] getChunksInRange(final Position position, int range) {
range = range * 2;

View File

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