
Currently we use the async chunk loading system only when players trigger chunk loading. If a chunk is loaded for any other reason it goes through a separate codepath. This means if a player has trigged a chunk load and before the chunk loads something else wants the same chunk we will load it twice and throw away the second result. With this change we instead use the sync processing feature of the AsynchronousExecutor to load the chunk which will pull it out of the queue if it was already queued to load. This means we only ever load a chunk once. Because chunk generation is not thread safe we still fallback to the vanilla chunk loading system if the chunk does not currently exist.
64 lines
2.4 KiB
Java
64 lines
2.4 KiB
Java
package org.bukkit.craftbukkit.chunkio;
|
|
|
|
import net.minecraft.server.Chunk;
|
|
import net.minecraft.server.ChunkRegionLoader;
|
|
import net.minecraft.server.NBTTagCompound;
|
|
|
|
import org.bukkit.Server;
|
|
import org.bukkit.craftbukkit.util.AsynchronousExecutor;
|
|
import org.bukkit.craftbukkit.util.LongHash;
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider<QueuedChunk, Chunk, Runnable, RuntimeException> {
|
|
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
|
|
|
// async stuff
|
|
public Chunk callStage1(QueuedChunk queuedChunk) throws RuntimeException {
|
|
ChunkRegionLoader loader = queuedChunk.loader;
|
|
Object[] data = loader.loadChunk(queuedChunk.world, queuedChunk.x, queuedChunk.z);
|
|
|
|
if (data != null) {
|
|
queuedChunk.compound = (NBTTagCompound) data[1];
|
|
return (Chunk) data[0];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// sync stuff
|
|
public void callStage2(QueuedChunk queuedChunk, Chunk chunk) throws RuntimeException {
|
|
if(chunk == null) {
|
|
// If the chunk loading failed just do it synchronously (may generate)
|
|
queuedChunk.provider.originalGetChunkAt(queuedChunk.x, queuedChunk.z);
|
|
return;
|
|
}
|
|
|
|
queuedChunk.loader.loadEntities(chunk, queuedChunk.compound.getCompound("Level"), queuedChunk.world);
|
|
chunk.p = queuedChunk.provider.world.getTime();
|
|
queuedChunk.provider.chunks.put(LongHash.toLong(queuedChunk.x, queuedChunk.z), chunk);
|
|
chunk.addEntities();
|
|
|
|
if (queuedChunk.provider.chunkProvider != null) {
|
|
queuedChunk.provider.chunkProvider.recreateStructures(queuedChunk.x, queuedChunk.z);
|
|
}
|
|
|
|
Server server = queuedChunk.provider.world.getServer();
|
|
if (server != null) {
|
|
server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(chunk.bukkitChunk, false));
|
|
}
|
|
|
|
chunk.a(queuedChunk.provider, queuedChunk.provider, queuedChunk.x, queuedChunk.z);
|
|
}
|
|
|
|
public void callStage3(QueuedChunk queuedChunk, Chunk chunk, Runnable runnable) throws RuntimeException {
|
|
runnable.run();
|
|
}
|
|
|
|
public Thread newThread(Runnable runnable) {
|
|
Thread thread = new Thread(runnable, "Chunk I/O Executor Thread-" + threadNumber.getAndIncrement());
|
|
thread.setDaemon(true);
|
|
return thread;
|
|
}
|
|
}
|