Added IChunkLoader#saveChunks with a default implementation

This commit is contained in:
themode 2020-10-24 09:34:19 +02:00
parent 4ddfc88d43
commit 3e59c9d396
3 changed files with 67 additions and 43 deletions

View File

@ -1,6 +1,13 @@
package net.minestom.server.instance;
import net.minestom.server.MinecraftServer;
import net.minestom.server.utils.callback.OptionalCallback;
import net.minestom.server.utils.chunk.ChunkCallback;
import net.minestom.server.utils.thread.MinestomThread;
import java.util.Collection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Interface implemented to change the way chunks are loaded/saved.
@ -10,26 +17,61 @@ import net.minestom.server.utils.chunk.ChunkCallback;
public interface IChunkLoader {
/**
* Load a {@link Chunk}, all blocks should be set since the {@link ChunkGenerator} is not applied
* Loads a {@link Chunk}, all blocks should be set since the {@link ChunkGenerator} is not applied.
*
* @param instance the {@link Instance} where the {@link Chunk} belong
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @param callback the callback executed when the {@link Chunk} is done loading,
* never called if the method returns false.
* never called if the method returns false. Can be null.
* @return true if the chunk loaded successfully, false otherwise
*/
boolean loadChunk(Instance instance, int chunkX, int chunkZ, ChunkCallback callback);
/**
* Save a {@link Chunk} with a callback for when it is done
* Saves a {@link Chunk} with an optional callback for when it is done.
*
* @param chunk the {@link Chunk} to save
* @param callback the callback executed when the {@link Chunk} is done saving,
* should be called even if the saving failed (you can throw an exception)
* should be called even if the saving failed (you can throw an exception).
* Can be null.
*/
void saveChunk(Chunk chunk, Runnable callback);
/**
* Saves multiple chunks with an optional callback for when it is done.
* <p>
* Implementations need to check {@link #supportsParallelSaving()} to support the feature if possible.
*
* @param chunks the chunks to save
* @param callback the callback executed when the {@link Chunk} is done saving,
* should be called even if the saving failed (you can throw an exception).
* Can be null.
*/
default void saveChunks(Collection<Chunk> chunks, Runnable callback) {
if (supportsParallelSaving()) {
ExecutorService parallelSavingThreadPool = new MinestomThread(MinecraftServer.THREAD_COUNT_PARALLEL_CHUNK_SAVING, MinecraftServer.THREAD_NAME_PARALLEL_CHUNK_SAVING, true);
chunks.forEach(c -> parallelSavingThreadPool.execute(() -> saveChunk(c, null)));
try {
parallelSavingThreadPool.shutdown();
parallelSavingThreadPool.awaitTermination(1L, java.util.concurrent.TimeUnit.DAYS);
OptionalCallback.execute(callback);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
AtomicInteger counter = new AtomicInteger();
for (Chunk chunk : chunks) {
saveChunk(chunk, () -> {
final boolean isLast = counter.incrementAndGet() == chunks.size();
if (isLast) {
OptionalCallback.execute(callback);
}
});
}
}
}
/**
* Does this {@link IChunkLoader} allow for multi-threaded saving of {@link Chunk}?
*

View File

@ -142,7 +142,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
*
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @param callback consumer called after the chunk has been generated,
* @param callback optional consumer called after the chunk has been generated,
* the returned chunk will never be null
*/
public abstract void loadChunk(int chunkX, int chunkZ, ChunkCallback callback);
@ -153,7 +153,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
*
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @param callback consumer called after the chunk has tried to be loaded,
* @param callback optional consumer called after the chunk has tried to be loaded,
* contains a chunk if it is successful, null otherwise
*/
public abstract void loadOptionalChunk(int chunkX, int chunkZ, ChunkCallback callback);
@ -184,14 +184,14 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
* Saves a {@link Chunk} to permanent storage.
*
* @param chunk the {@link Chunk} to save
* @param callback called when the {@link Chunk} is done saving
* @param callback optional callback called when the {@link Chunk} is done saving
*/
public abstract void saveChunkToStorage(Chunk chunk, Runnable callback);
/**
* Saves multiple chunks to permanent storage.
*
* @param callback called when the chunks are done saving
* @param callback optional callback called when the chunks are done saving
*/
public abstract void saveChunksToStorage(Runnable callback);
@ -254,7 +254,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
*
* @param chunkX the chunk X
* @param chunkZ the chunk X
* @param callback the callback executed once the {@link Chunk} has been retrieved
* @param callback the optional callback executed once the {@link Chunk} has been retrieved
*/
protected abstract void retrieveChunk(int chunkX, int chunkZ, ChunkCallback callback);
@ -265,7 +265,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
*
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @param callback the callback executed with the newly created {@link Chunk}
* @param callback the optional callback executed with the newly created {@link Chunk}
*/
protected abstract void createChunk(int chunkX, int chunkZ, ChunkCallback callback);
@ -541,7 +541,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
* Loads the chunk at the given {@link Position} with a callback.
*
* @param position the chunk position
* @param callback the callback to run when the chunk is loaded
* @param callback the optional callback to run when the chunk is loaded
*/
public void loadChunk(Position position, ChunkCallback callback) {
final int chunkX = ChunkUtils.getChunkCoordinate((int) position.getX());
@ -554,7 +554,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
* at the given {@link Position} with a callback.
*
* @param position the chunk position
* @param callback the callback executed when the chunk is loaded (or with a null chunk if not)
* @param callback the optional callback executed when the chunk is loaded (or with a null chunk if not)
*/
public void loadOptionalChunk(Position position, ChunkCallback callback) {
final int chunkX = ChunkUtils.getChunkCoordinate((int) position.getX());
@ -933,7 +933,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
* Schedules a block update at a given {@link BlockPosition}.
* Does nothing if no {@link CustomBlock} is present at 'position'.
* <p>
* Cancelled if the block changes between this call and the actual update
* Cancelled if the block changes between this call and the actual update.
*
* @param time in how long this update must be performed?
* @param unit in what unit is the time expressed
@ -946,7 +946,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
* <p>
* Warning: this does not update chunks and entities.
*
* @param time the current time
* @param time the tick time in milliseconds
*/
public void tick(long time) {
// scheduled tasks

View File

@ -25,7 +25,6 @@ import net.minestom.server.utils.callback.OptionalCallback;
import net.minestom.server.utils.chunk.ChunkCallback;
import net.minestom.server.utils.chunk.ChunkSupplier;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.thread.MinestomThread;
import net.minestom.server.utils.time.TimeUnit;
import net.minestom.server.utils.validate.Check;
import net.minestom.server.world.DimensionType;
@ -34,7 +33,6 @@ 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;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@ -221,11 +219,12 @@ public class InstanceContainer extends Instance {
}
/**
* Has this block already changed since last update? Prevents StackOverflow with blocks trying to modify their position in onDestroy or onPlace.
* Has this block already changed since last update?
* Prevents StackOverflow with blocks trying to modify their position in onDestroy or onPlace.
*
* @param blockPosition the block position
* @param blockStateId the block state id
* @return
* @return true if the block changed since the last update
*/
private boolean isAlreadyChanged(BlockPosition blockPosition, short blockStateId) {
final Block changedBlock = currentlyChangingBlocks.get(blockPosition);
@ -248,7 +247,7 @@ public class InstanceContainer extends Instance {
/**
* Calls {@link CustomBlock#onDestroy(Instance, BlockPosition, Data)} for {@code previousBlock}.
* <p>
* WARNING {@code chunk} needs to be synchronized
* WARNING {@code chunk} needs to be synchronized.
*
* @param chunk the chunk where the block is
* @param index the index of the block
@ -263,7 +262,7 @@ public class InstanceContainer extends Instance {
/**
* Calls {@link CustomBlock#onPlace(Instance, BlockPosition, Data)} for the current custom block at the position.
* <p>
* WARNING {@code chunk} needs to be synchronized
* WARNING {@code chunk} needs to be synchronized.
*
* @param chunk the chunk where the block is
* @param index the block index
@ -444,9 +443,9 @@ public class InstanceContainer extends Instance {
/**
* Saves the instance ({@link #getUniqueId()} {@link #getData()}) and call {@link #saveChunksToStorage(Runnable)}.
* <p>
* WARNING: {@link #getData()} needs to be a {@link SerializableData} in order to be saved
* WARNING: {@link #getData()} needs to be a {@link SerializableData} in order to be saved.
*
* @param callback the callback once the saving is done
* @param callback the callback once the saving is done. Can be null.
*/
public void saveInstance(Runnable callback) {
Check.notNull(getStorageLocation(), "You cannot save the instance if no StorageLocation has been defined");
@ -464,7 +463,7 @@ public class InstanceContainer extends Instance {
}
/**
* Save the instance without callback
* Save the instance without callback.
*
* @see #saveInstance(Runnable)
*/
@ -474,29 +473,12 @@ public class InstanceContainer extends Instance {
@Override
public void saveChunkToStorage(Chunk chunk, Runnable callback) {
chunkLoader.saveChunk(chunk, callback);
this.chunkLoader.saveChunk(chunk, callback);
}
@Override
public void saveChunksToStorage(Runnable callback) {
if (chunkLoader.supportsParallelSaving()) {
ExecutorService parallelSavingThreadPool = new MinestomThread(MinecraftServer.THREAD_COUNT_PARALLEL_CHUNK_SAVING, MinecraftServer.THREAD_NAME_PARALLEL_CHUNK_SAVING, true);
getChunks().forEach(c -> parallelSavingThreadPool.execute(() -> saveChunkToStorage(c, null)));
try {
parallelSavingThreadPool.shutdown();
parallelSavingThreadPool.awaitTermination(1L, java.util.concurrent.TimeUnit.DAYS);
OptionalCallback.execute(callback);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
final Iterator<Chunk> chunkIterator = chunks.values().iterator();
while (chunkIterator.hasNext()) {
final Chunk chunk = chunkIterator.next();
final boolean isLast = !chunkIterator.hasNext();
saveChunkToStorage(chunk, isLast ? callback : null);
}
}
this.chunkLoader.saveChunks(chunks.values(), callback);
}
@Override
@ -579,7 +561,7 @@ public class InstanceContainer extends Instance {
* <p>
* WARNING: if you need to save this instance's chunks later,
* the code needs to be predictable for {@link IChunkLoader#loadChunk(Instance, int, int, ChunkCallback)}
* to create the correct type of {@link Chunk}. tl;dr: Need chunk save = no random
* to create the correct type of {@link Chunk}. tl;dr: Need chunk save = no random type.
*
* @param chunkSupplier the new {@link ChunkSupplier} of this instance, chunks need to be non-null
* @throws NullPointerException if {@code chunkSupplier} is null