Reduce chunk entities Set allocation

This commit is contained in:
TheMode 2021-07-21 22:21:43 +02:00
parent 266ecd5b64
commit a0bb437c4c
3 changed files with 30 additions and 17 deletions

View File

@ -1434,10 +1434,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
*/ */
public void refreshVisibleChunks(@NotNull Chunk newChunk) { public void refreshVisibleChunks(@NotNull Chunk newChunk) {
// Previous chunks indexes // Previous chunks indexes
final long[] lastVisibleChunks = viewableChunks.stream().mapToLong(viewableChunks -> final long[] lastVisibleChunks = viewableChunks.stream().mapToLong(ChunkUtils::getChunkIndex).toArray();
ChunkUtils.getChunkIndex(viewableChunks.getChunkX(), viewableChunks.getChunkZ())
).toArray();
// New chunks indexes // New chunks indexes
final long[] updatedVisibleChunks = ChunkUtils.getChunksInRange(newChunk.toPosition(), getChunkRange()); final long[] updatedVisibleChunks = ChunkUtils.getChunksInRange(newChunk.toPosition(), getChunkRange());

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 net.kyori.adventure.identity.Identity; import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.pointer.Pointers; import net.kyori.adventure.pointer.Pointers;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
@ -83,8 +85,8 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
protected final Set<EntityCreature> creatures = ConcurrentHashMap.newKeySet(); protected final Set<EntityCreature> creatures = ConcurrentHashMap.newKeySet();
protected final Set<ExperienceOrb> experienceOrbs = ConcurrentHashMap.newKeySet(); protected final Set<ExperienceOrb> experienceOrbs = ConcurrentHashMap.newKeySet();
// Entities per chunk // Entities per chunk
protected final Map<Long, Set<Entity>> chunkEntities = new ConcurrentHashMap<>(); protected final Object entitiesLock = new Object(); // Lock used to prevent the entities Set and Map to be subject to race condition
private final Object entitiesLock = new Object(); // Lock used to prevent the entities Set and Map to be subject to race condition protected final Long2ObjectMap<Set<Entity>> chunkEntities = new Long2ObjectOpenHashMap<>();
// the uuid of this instance // the uuid of this instance
protected UUID uniqueId; protected UUID uniqueId;
@ -486,9 +488,12 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
public @NotNull Set<Entity> getChunkEntities(Chunk chunk) { public @NotNull Set<Entity> getChunkEntities(Chunk chunk) {
if (!ChunkUtils.isLoaded(chunk)) if (!ChunkUtils.isLoaded(chunk))
return Collections.emptySet(); return Collections.emptySet();
final Set<Entity> entities;
final long index = ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ()); synchronized (entitiesLock) {
final Set<Entity> entities = getEntitiesInChunk(index); if ((entities = chunkEntities.get(ChunkUtils.getChunkIndex(chunk))) == null) {
return Collections.emptySet();
}
}
return Collections.unmodifiableSet(entities); return Collections.unmodifiableSet(entities);
} }
@ -642,15 +647,15 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
final long oldIndex = ChunkUtils.getChunkIndex(lastChunk); final long oldIndex = ChunkUtils.getChunkIndex(lastChunk);
final long newIndex = ChunkUtils.getChunkIndex(newChunk); final long newIndex = ChunkUtils.getChunkIndex(newChunk);
synchronized (entitiesLock) { synchronized (entitiesLock) {
getEntitiesInChunk(oldIndex).remove(entity); removeEntityChunk(oldIndex, entity);
getEntitiesInChunk(newIndex).add(entity); addEntityChunk(newIndex, entity);
} }
} }
private void UNSAFE_addEntityToChunk(@NotNull Entity entity, @NotNull Chunk chunk) { private void UNSAFE_addEntityToChunk(@NotNull Entity entity, @NotNull Chunk chunk) {
final long chunkIndex = ChunkUtils.getChunkIndex(chunk); final long chunkIndex = ChunkUtils.getChunkIndex(chunk);
synchronized (entitiesLock) { synchronized (entitiesLock) {
getEntitiesInChunk(chunkIndex).add(entity); addEntityChunk(chunkIndex, entity);
this.entities.add(entity); this.entities.add(entity);
if (entity instanceof Player) { if (entity instanceof Player) {
this.players.add((Player) entity); this.players.add((Player) entity);
@ -665,7 +670,7 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
private void UNSAFE_removeEntityFromChunk(@NotNull Entity entity, @NotNull Chunk chunk) { private void UNSAFE_removeEntityFromChunk(@NotNull Entity entity, @NotNull Chunk chunk) {
final long chunkIndex = ChunkUtils.getChunkIndex(chunk); final long chunkIndex = ChunkUtils.getChunkIndex(chunk);
synchronized (entitiesLock) { synchronized (entitiesLock) {
getEntitiesInChunk(chunkIndex).remove(entity); removeEntityChunk(chunkIndex, entity);
this.entities.remove(entity); this.entities.remove(entity);
if (entity instanceof Player) { if (entity instanceof Player) {
this.players.remove(entity); this.players.remove(entity);
@ -677,9 +682,18 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
} }
} }
@NotNull private void addEntityChunk(long index, Entity entity) {
private Set<Entity> getEntitiesInChunk(long index) { this.chunkEntities.computeIfAbsent(index, i -> ConcurrentHashMap.newKeySet()).add(entity);
return chunkEntities.computeIfAbsent(index, i -> ConcurrentHashMap.newKeySet()); }
private void removeEntityChunk(long index, Entity entity) {
var chunkEntities = this.chunkEntities.get(index);
if (chunkEntities != null) {
chunkEntities.remove(entity);
if (chunkEntities.isEmpty()) {
this.chunkEntities.remove(index);
}
}
} }
/** /**

View File

@ -566,7 +566,9 @@ public class InstanceContainer extends Instance {
// Clear cache // Clear cache
this.chunks.remove(index); this.chunks.remove(index);
this.chunkEntities.remove(index); synchronized (entitiesLock){
this.chunkEntities.remove(index);
}
chunk.unload(); chunk.unload();