Small cleanup and reduce memory usage with AbsoluteBlockBatch

This commit is contained in:
themode 2021-01-08 07:08:10 +01:00 committed by Matt Worzala
parent c35b8887e8
commit 6bd09256f3
3 changed files with 29 additions and 21 deletions

View File

@ -1,5 +1,7 @@
package net.minestom.server.instance.batch.v2;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minestom.server.data.Data;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.InstanceContainer;
@ -7,12 +9,11 @@ import net.minestom.server.utils.chunk.ChunkUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A Batch which can be used when changes are required across chunk borders,
* A {@link Batch} which can be used when changes are required across chunk borders,
* but the changes do not need any translation. If translation is required,
* use a {@link RelativeBlockBatch} instead.
* <p>
@ -24,9 +25,10 @@ import java.util.concurrent.atomic.AtomicInteger;
public class AbsoluteBlockBatch implements Batch<Runnable> {
// In the form of <Chunk Index, Batch>
private final Map<Long, ChunkBatch> data = new HashMap<>();
private final Long2ObjectMap<ChunkBatch> chunkBatchesMap = new Long2ObjectOpenHashMap<>();
public AbsoluteBlockBatch() {}
public AbsoluteBlockBatch() {
}
@Override
public void setSeparateBlocks(int x, int y, int z, short blockStateId, short customBlockId, @Nullable Data data) {
@ -34,7 +36,10 @@ public class AbsoluteBlockBatch implements Batch<Runnable> {
final int chunkZ = ChunkUtils.getChunkCoordinate(z);
final long chunkIndex = ChunkUtils.getChunkIndex(chunkX, chunkZ);
final ChunkBatch chunkBatch = this.data.computeIfAbsent(chunkIndex, i -> new ChunkBatch());
final ChunkBatch chunkBatch;
synchronized (chunkBatchesMap) {
chunkBatch = chunkBatchesMap.computeIfAbsent(chunkIndex, i -> new ChunkBatch());
}
final int relativeX = x - (chunkX * Chunk.CHUNK_SIZE_X);
final int relativeZ = z - (chunkZ * Chunk.CHUNK_SIZE_Z);
@ -43,13 +48,13 @@ public class AbsoluteBlockBatch implements Batch<Runnable> {
@Override
public void clear() {
synchronized (data) {
this.data.clear();
synchronized (chunkBatchesMap) {
this.chunkBatchesMap.clear();
}
}
/**
* Apply this batch to the given instance.
* Applies this batch to the given instance.
*
* @param instance The instance in which the batch should be applied
* @param callback The callback to be executed when the batch is applied
@ -60,7 +65,7 @@ public class AbsoluteBlockBatch implements Batch<Runnable> {
}
/**
* Apply this batch to the given instance, and execute the callback immediately when the
* Applies this batch to the given instance, and execute the callback immediately when the
* blocks have been applied, in an unknown thread.
*
* @param instance The instance in which the batch should be applied
@ -71,32 +76,33 @@ public class AbsoluteBlockBatch implements Batch<Runnable> {
}
/**
* Apply this batch to the given instance, and execute the callback depending on safeCallback.
* Applies this batch to the given instance, and execute the callback depending on safeCallback.
*
* @param instance The instance in which the batch should be applied
* @param callback The callback to be executed when the batch is applied
* @param instance The instance in which the batch should be applied
* @param callback The callback to be executed when the batch is applied
* @param safeCallback If true, the callback will be executed in the next instance update. Otherwise it will be executed immediately upon completion
*
*/
protected void apply(@NotNull InstanceContainer instance, @Nullable Runnable callback, boolean safeCallback) {
synchronized (data) {
final AtomicInteger counter = new AtomicInteger();
for (Map.Entry<Long, ChunkBatch> entry : data.entrySet()) {
synchronized (chunkBatchesMap) {
AtomicInteger counter = new AtomicInteger();
for (Map.Entry<Long, ChunkBatch> entry : chunkBatchesMap.long2ObjectEntrySet()) {
final long chunkIndex = entry.getKey();
final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex);
final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex);
final ChunkBatch batch = entry.getValue();
batch.apply(instance, chunkX, chunkZ, c -> {
final boolean isLast = counter.incrementAndGet() == data.size();
final boolean isLast = counter.incrementAndGet() == chunkBatchesMap.size();
// Execute the callback if this was the last chunk to process
if (isLast) {
instance.refreshLastBlockChangeTime();
if (callback != null) {
if (safeCallback)
if (safeCallback) {
instance.scheduleNextTick(inst -> callback.run());
else callback.run();
} else {
callback.run();
}
}
}
});

View File

@ -16,7 +16,7 @@ import java.util.concurrent.ExecutorService;
* A Batch is a tool used to cache a list of block changes, and apply the changes whenever you want.
* <p>
* Batches offer a performance benefit because clients are not notified of any change until all of
* the blocks have been placed.
* the blocks have been placed, and because changes can happen with less synchronization.
* <p>
* All batches may be rotated using {link}, however rotate operations do not mutate the batch, so the
* result should be cached if used multiple times.
@ -30,6 +30,7 @@ import java.util.concurrent.ExecutorService;
* @see RelativeBlockBatch
*/
public interface Batch<Callback> extends BlockModifier {
ExecutorService BLOCK_BATCH_POOL = new MinestomThread(MinecraftServer.THREAD_COUNT_BLOCK_BATCH, MinecraftServer.THREAD_NAME_BLOCK_BATCH);
@Override

View File

@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory;
* @see Batch
*/
public class ChunkBatch implements Batch<ChunkCallback> {
private static final Logger LOGGER = LoggerFactory.getLogger(ChunkBatch.class);
// Need to be synchronized manually
@ -187,7 +188,7 @@ public class ChunkBatch implements Batch<ChunkCallback> {
/**
* Updates the given chunk for all of its viewers, and executes the callback.
*/
private void updateChunk(InstanceContainer instance, Chunk chunk, @Nullable ChunkCallback callback, boolean safeCallback) {
private void updateChunk(@NotNull InstanceContainer instance, @NotNull Chunk chunk, @Nullable ChunkCallback callback, boolean safeCallback) {
// Refresh chunk for viewers
// Formerly this had an option to do a Chunk#sendChunkUpdate