diff --git a/src/main/java/net/minecraft/world/World.java b/src/main/java/net/minecraft/world/World.java index 840c26a..d5e3af6 100644 --- a/src/main/java/net/minecraft/world/World.java +++ b/src/main/java/net/minecraft/world/World.java @@ -18,6 +18,7 @@ import org.ultramine.server.ConfigurationHandler; import org.ultramine.server.ServerLoadBalancer; +import org.ultramine.server.chunk.CallbackAddDependency; import org.ultramine.server.chunk.ChunkHash; import org.ultramine.server.chunk.IChunkLoadCallback; @@ -2817,9 +2818,15 @@ for(ChunkCoordIntPair c : getPersistentChunks().keySet()) { if(chunkRoundExists(c.chunkXPos, c.chunkZPos, 1)) + { activeChunkSet.put(ChunkHash.chunkToKey(c.chunkXPos, c.chunkZPos), (byte)100); + } else - ((ChunkProviderServer)chunkProvider).loadAsyncRadius(c.chunkXPos, c.chunkZPos, 1, IChunkLoadCallback.EMPTY); + { + Chunk dep = getChunkIfExists(c.chunkXPos, c.chunkZPos); + if(dep != null) + ((ChunkProviderServer)chunkProvider).loadAsyncRadius(c.chunkXPos, c.chunkZPos, 1, new CallbackAddDependency(dep)); + } } } int i; diff --git a/src/main/java/net/minecraft/world/chunk/Chunk.java b/src/main/java/net/minecraft/world/chunk/Chunk.java index 4067eaf..be7a323 100644 --- a/src/main/java/net/minecraft/world/chunk/Chunk.java +++ b/src/main/java/net/minecraft/world/chunk/Chunk.java @@ -48,9 +48,10 @@ import org.apache.logging.log4j.Logger; import org.ultramine.server.chunk.ChunkBindState; import org.ultramine.server.chunk.ChunkHash; +import org.ultramine.server.chunk.IChunkDependency; import org.ultramine.server.chunk.PendingBlockUpdate; -public class Chunk +public class Chunk implements IChunkDependency { private static final Logger logger = LogManager.getLogger(); public static boolean isLit; @@ -1536,6 +1537,7 @@ private TreeSet pendingUpdatesQueue; private ChunkBindState bindState = ChunkBindState.NONE; + private List dependencies = new ArrayList(2); private int loadTime; private int unbindTime; private boolean wasActive; @@ -1601,6 +1603,45 @@ this.bindState = bindState; } + public void addDependency(IChunkDependency dep) + { + if(!dependencies.contains(dep) && dep != this && dep != null) + dependencies.add(dep); + } + + public boolean isDependent() + { + boolean isdependent = false; + + for(int i = 0; i < dependencies.size(); i++) + { + IChunkDependency dep = dependencies.get(i); + if(dep.isDependent(this)) + { + isdependent = true; //Не возвращаем значение сразу, проходимся по остальным, удаляем лишнее + } + else + { + dependencies.remove(i--); + updateUnbindTime(); + } + + } + + return isdependent; + } + + public boolean canUnload() + { + return !isDependent() && bindState.canUnload(); + } + + @Override + public boolean isDependent(Chunk chunk) + { + return !canUnload(); + } + public void unbind() { if(bindState.canChangeState()) diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java index b755781..cc043b5 100644 --- a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java @@ -41,6 +41,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.ultramine.server.chunk.CallbackMultiChunkDependentTask; import org.ultramine.server.chunk.ChunkBindState; import org.ultramine.server.chunk.ChunkGC; import org.ultramine.server.chunk.ChunkHash; @@ -86,7 +87,7 @@ if(chunk != null) { chunk.unbind(); - if(chunk.getBindState().canUnload()) + if(chunk.canUnload()) chunksToUnload.add(ChunkHash.chunkToKey(par1, par2)); } } @@ -358,7 +359,7 @@ Chunk chunk = loadedChunkHashMap.get(hash); if(chunk != null) { - if(chunk.getBindState().canUnload() && !persistentChunks.contains(chunk.getChunkCoordIntPair())) + if(chunk.canUnload() && !persistentChunks.contains(chunk.getChunkCoordIntPair())) { chunk.onChunkUnload(); if(chunk.shouldSaveOnUnload()) @@ -442,6 +443,9 @@ loadAsync(x, z, IChunkLoadCallback.EMPTY); } + /** + * Загружает все чанки в радиусе, callback вызывается для каждого чанка + */ public void loadAsyncRadius(int cx, int cz, int radius, IChunkLoadCallback callback) { for(int x = cx - radius; x <= cx + radius; x++) @@ -449,6 +453,30 @@ loadAsync(x, z, callback); } + /** + * Загружает все чанки в радиусе, callback вызывается, когда все чанки загружены + */ + public void loadAsyncRadiusThenRun(int cx, int cz, int radius, Runnable callback) + { + loadAsyncRadius(cx, cz, radius, new CallbackMultiChunkDependentTask((radius*2+1)*(radius*2+1), callback)); + } + + /** + * Загружает все чанки в радиусе, callback вызывается только для центрального чанка (cx, cz), + * когда все чанки загружены + */ + public void loadAsyncWithRadius(final int cx, final int cz, int radius, final IChunkLoadCallback callback) + { + loadAsyncRadiusThenRun(cx, cz, radius, new Runnable() + { + @Override + public void run() + { + callback.onChunkLoaded(loadedChunkHashMap.get(cx, cz)); + } + }); + } + public Chunk getChunkIfExists(int cx, int cz) { return loadedChunkHashMap.get(cx, cz); diff --git a/src/main/java/org/ultramine/server/chunk/CallbackAddDependency.java b/src/main/java/org/ultramine/server/chunk/CallbackAddDependency.java new file mode 100644 index 0000000..3d5fce2 --- /dev/null +++ b/src/main/java/org/ultramine/server/chunk/CallbackAddDependency.java @@ -0,0 +1,31 @@ +package org.ultramine.server.chunk; + +import net.minecraft.world.chunk.Chunk; + +public class CallbackAddDependency implements IChunkLoadCallback +{ + private final IChunkDependency dependency; + + public CallbackAddDependency(IChunkDependency dependency) + { + this.dependency = dependency; + } + + @Override + public void onChunkLoaded(Chunk chunk) + { + chunk.addDependency(dependency); + } + + @Override + public int hashCode() + { + return dependency.hashCode(); + } + + @Override + public boolean equals(Object o) + { + return o instanceof CallbackAddDependency && ((CallbackAddDependency)o).dependency.equals(dependency); + } +} diff --git a/src/main/java/org/ultramine/server/chunk/CallbackMultiChunkDependentTask.java b/src/main/java/org/ultramine/server/chunk/CallbackMultiChunkDependentTask.java new file mode 100644 index 0000000..ae14699 --- /dev/null +++ b/src/main/java/org/ultramine/server/chunk/CallbackMultiChunkDependentTask.java @@ -0,0 +1,31 @@ +package org.ultramine.server.chunk; + +import net.minecraft.world.chunk.Chunk; + +public class CallbackMultiChunkDependentTask implements IChunkLoadCallback, IChunkDependency +{ + private int numChunksForLoad; + private final Runnable task; + + public CallbackMultiChunkDependentTask(int numChunksForLoad, Runnable task) + { + this.numChunksForLoad = numChunksForLoad; + this.task = task; + } + + @Override + public void onChunkLoaded(Chunk chunk) + { + chunk.addDependency(this); + if(numChunksForLoad == 1) + task.run(); + + numChunksForLoad--; + } + + @Override + public boolean isDependent(Chunk chunk) + { + return numChunksForLoad != 0; + } +} diff --git a/src/main/java/org/ultramine/server/chunk/ChunkGC.java b/src/main/java/org/ultramine/server/chunk/ChunkGC.java index 00863c3..383086f 100644 --- a/src/main/java/org/ultramine/server/chunk/ChunkGC.java +++ b/src/main/java/org/ultramine/server/chunk/ChunkGC.java @@ -55,6 +55,8 @@ List unbound = new ArrayList(all.size() - boundChunks); for(Chunk chunk : all) { + if(chunk.isDependent()) + continue; ChunkBindState state = chunk.getBindState(); if(state.canUnload()) { diff --git a/src/main/java/org/ultramine/server/chunk/ChunkSendManager.java b/src/main/java/org/ultramine/server/chunk/ChunkSendManager.java index af574c4..b68136c 100644 --- a/src/main/java/org/ultramine/server/chunk/ChunkSendManager.java +++ b/src/main/java/org/ultramine/server/chunk/ChunkSendManager.java @@ -13,6 +13,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.ultramine.server.util.BlockFace; import org.ultramine.server.util.ChunkCoordComparator; import org.ultramine.server.util.TIntArrayListImpl; @@ -32,6 +34,7 @@ public class ChunkSendManager { + private static final Logger log = LogManager.getLogger(); private static final ExecutorService executor = Executors.newFixedThreadPool(1); private static final int MAX_QUEUE_SIZE = 8; private static final int DEFAULT_RATE = 3; @@ -274,7 +277,7 @@ sendingQueueSize.incrementAndGet(); int ncx = ChunkHash.keyToX(key); int ncz = ChunkHash.keyToZ(key); - manager.getWorldServer().theChunkProviderServer.loadAsync(ncx, ncz, chunkLoadCallback); + manager.getWorldServer().theChunkProviderServer.loadAsyncWithRadius(ncx, ncz, 1, chunkLoadCallback); } toSend.remove(0, count); } @@ -371,22 +374,20 @@ return; } - if(chunk.isTerrainPopulated && chunk.worldObj.chunkRoundExists(chunk.xPosition, chunk.zPosition, 1)) + if(chunk.isTerrainPopulated) { chunk.func_150804_b(true); chunk.setBindState(ChunkBindState.PLAYER); executor.execute(new CompressAndSendChunkTask(chunk)); } - else + else if(!chunk.worldObj.chunkRoundExists(chunk.xPosition, chunk.zPosition, 2)) { + ((WorldServer)chunk.worldObj).theChunkProviderServer.loadAsyncWithRadius(chunk.xPosition, chunk.zPosition, 2, this); + } + else //impossible? + { + log.fatal("Chunk[{}]({}, {}) not populated when loaded {} chunk radius", chunk.worldObj.provider.dimensionId, chunk.xPosition, chunk.zPosition, 2); sendingQueueSize.decrementAndGet(); - ((WorldServer)chunk.worldObj).theChunkProviderServer.loadAsyncRadius(chunk.xPosition, chunk.zPosition, 2, IChunkLoadCallback.EMPTY); - - int ind = (int)rate*2+1; - if(ind < toSend.size()-1) - toSend.insert(ind, key); - else - toSend.add(key); sending.remove(key); } } diff --git a/src/main/java/org/ultramine/server/chunk/IChunkDependency.java b/src/main/java/org/ultramine/server/chunk/IChunkDependency.java new file mode 100644 index 0000000..9ea3ee1 --- /dev/null +++ b/src/main/java/org/ultramine/server/chunk/IChunkDependency.java @@ -0,0 +1,8 @@ +package org.ultramine.server.chunk; + +import net.minecraft.world.chunk.Chunk; + +public interface IChunkDependency +{ + boolean isDependent(Chunk chunk); +}