diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java index 211e5d9..ecccb8a 100644 --- a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java @@ -46,6 +46,7 @@ import org.ultramine.server.chunk.CallbackMultiChunkDependentTask; import org.ultramine.server.chunk.ChunkBindState; import org.ultramine.server.chunk.ChunkGC; +import org.ultramine.server.chunk.ChunkGenerationQueue; import org.ultramine.server.chunk.ChunkHash; import org.ultramine.server.chunk.ChunkMap; import org.ultramine.server.chunk.IChunkLoadCallback; @@ -480,7 +481,7 @@ return chunkGC; } - public void loadAsync(int x, int z, IChunkLoadCallback callback) + public boolean loadAsync(int x, int z, boolean generateOnRequest, IChunkLoadCallback callback) { Chunk chunk = chunkMap.get(x, z); if(chunk != null) @@ -491,10 +492,22 @@ { ChunkIOExecutor.queueChunkLoad(this.worldObj, (AnvilChunkLoader)currentChunkLoader, this, x, z, callback); } - else + else if(generateOnRequest) { callback.onChunkLoaded(originalLoadChunk(x, z)); } + else + { + return false; + } + + return true; + } + + public void loadAsync(int x, int z, IChunkLoadCallback callback) + { + if(!loadAsync(x, z, false, callback)) + ChunkGenerationQueue.instance().queueChunkGeneration(this, x, z, callback); } public void loadAsync(int x, int z) diff --git a/src/main/java/org/ultramine/server/UltramineServerModContainer.java b/src/main/java/org/ultramine/server/UltramineServerModContainer.java index 64ac8f8..fac59c9 100644 --- a/src/main/java/org/ultramine/server/UltramineServerModContainer.java +++ b/src/main/java/org/ultramine/server/UltramineServerModContainer.java @@ -19,6 +19,7 @@ import org.ultramine.permission.IPermissionManager; import org.ultramine.permission.MinecraftPermissions; import org.ultramine.permission.commands.BasicPermissionCommands; +import org.ultramine.server.chunk.ChunkGenerationQueue; import org.ultramine.server.chunk.ChunkProfiler; import org.ultramine.server.data.Databases; import org.ultramine.server.data.ServerDataLoader; @@ -145,6 +146,7 @@ { try { + ChunkGenerationQueue.instance().register(); e.getServer().getMultiWorld().register(); if(e.getSide().isServer()) { @@ -240,6 +242,7 @@ try { MinecraftServer.getServer().getMultiWorld().unregister(); + ChunkGenerationQueue.instance().unregister(); ChunkProfiler.instance().setEnabled(false); if(e.getSide().isServer()) diff --git a/src/main/java/org/ultramine/server/chunk/ChunkGenerationQueue.java b/src/main/java/org/ultramine/server/chunk/ChunkGenerationQueue.java new file mode 100644 index 0000000..a5e9713 --- /dev/null +++ b/src/main/java/org/ultramine/server/chunk/ChunkGenerationQueue.java @@ -0,0 +1,100 @@ +package org.ultramine.server.chunk; + +import com.google.common.collect.Queues; +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.TickEvent; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.gen.ChunkProviderServer; +import net.openhft.koloboke.collect.map.IntObjMap; +import net.openhft.koloboke.collect.map.hash.HashIntObjMaps; + +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; + +public class ChunkGenerationQueue +{ + private static final ChunkGenerationQueue INSTANCE = new ChunkGenerationQueue(); + private final Queue queue = Queues.newArrayDeque(); + private final IntObjMap map = HashIntObjMaps.newMutableMap(); + + public static ChunkGenerationQueue instance() + { + return INSTANCE; + } + + public void queueChunkGeneration(ChunkProviderServer provider, int cx, int cz, IChunkLoadCallback callback) + { + int key = ChunkHash.chunkToKey(cx, cz); + QueuedChunk chunk = map.get(key); + if(chunk != null) + { + chunk.callbacks.add(callback); + } + else + { + chunk = new QueuedChunk(provider, cx, cz, callback); + map.put(key, chunk); + queue.add(chunk); + } + } + + public boolean generateOneChunk() + { + for(QueuedChunk chunk; (chunk = queue.poll()) != null;) + { + map.remove(ChunkHash.chunkToKey(chunk.cx, chunk.cz)); + if(chunk.provider.isWorldUnloaded()) + continue; + if(chunk.provider.loadAsync(chunk.cx, chunk.cz, false, chunk)) + continue; + chunk.onChunkLoaded(chunk.provider.originalLoadChunk(chunk.cx, chunk.cz)); + return true; + } + + return false; + } + + public void register() + { + FMLCommonHandler.instance().bus().register(this); + } + + public void unregister() + { + FMLCommonHandler.instance().bus().unregister(this); + queue.clear(); + map.clear(); + } + + @SubscribeEvent + public void onTick(TickEvent.ServerTickEvent e) + { + if(e.phase == TickEvent.Phase.END) + generateOneChunk(); + } + + private static class QueuedChunk implements IChunkLoadCallback + { + public final ChunkProviderServer provider; + public final int cx; + public final int cz; + public final List callbacks = new ArrayList<>(1); + + public QueuedChunk(ChunkProviderServer provider, int cx, int cz, IChunkLoadCallback callback) + { + this.provider = provider; + this.cx = cx; + this.cz = cz; + this.callbacks.add(callback); + } + + @Override + public void onChunkLoaded(Chunk chunk) + { + for(IChunkLoadCallback cb : callbacks) + cb.onChunkLoaded(chunk); + } + } +}