mirror of
https://github.com/songoda/FabledSkyBlock.git
synced 2024-11-20 09:15:29 +01:00
Fix issue causing island scans never finish.
There was an issue in 1.8 specifically(where PaperLib doesn't has async chunks) that caused a similar error to this https://pastebin.com/tHtKdH5y. The issue was caused by the async task trying to take an snapshot of a chunk that couldn't be loaded async correctly, which caused issues like the shown above. The fix works in the next way. All the chunks that don't have an available snapshot are sent into a pendingChunks list where a sync task will try to process a certain amount of chunks every tick. After the async processing is finished, the task remains waiting for the pending chunks to be available. When all the pending chunks are ready to be processed(have an available snapshot) the async task resumes and process the chunks that were pending.
This commit is contained in:
parent
41e87c55b9
commit
489bfd14bc
@ -7,6 +7,7 @@ import com.songoda.skyblock.SkyBlock;
|
||||
import com.songoda.skyblock.island.Island;
|
||||
import com.songoda.skyblock.island.IslandEnvironment;
|
||||
import com.songoda.skyblock.world.WorldManager;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.Location;
|
||||
@ -15,26 +16,33 @@ import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public final class BlockScanner extends BukkitRunnable {
|
||||
|
||||
private static final Method ID_FIELD;
|
||||
private static final int MAX_CHUNKS_PER_ITERATION = 2;
|
||||
private static final int MAX_EMPTY_ITERATIONS = 20;
|
||||
|
||||
static {
|
||||
Method temp = null;
|
||||
|
||||
try {
|
||||
temp = ChunkSnapshot.class.getMethod("getBlockTypeId", int.class, int.class, int.class);
|
||||
} catch (NoSuchMethodException ignored) {}
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
}
|
||||
|
||||
ID_FIELD = temp;
|
||||
}
|
||||
@ -122,7 +130,22 @@ public final class BlockScanner extends BukkitRunnable {
|
||||
private void queueWork(World world, int scanY, List<CachedChunk> subList) {
|
||||
WorldManager worldManager = SkyBlock.getInstance().getWorldManager();
|
||||
|
||||
// The chunks that couldn't be taken snapshot async
|
||||
List<CachedChunk> pendingChunks = new ArrayList<>();
|
||||
|
||||
// The chunks that are ready to be processed asynchronously
|
||||
List<CachedChunk> readyChunks = new ArrayList<>();
|
||||
|
||||
// This lock will help to make the bukkit task wait after all the chunks that could be processed async are processed
|
||||
Lock lock = new ReentrantLock();
|
||||
|
||||
// This is the actual object that we will use to wait
|
||||
Condition emptyCondition = lock.newCondition();
|
||||
|
||||
Bukkit.getServer().getScheduler().runTaskAsynchronously(SkyBlock.getInstance(), () -> {
|
||||
// We need to hold the lock on the thread calling the await
|
||||
lock.lock();
|
||||
|
||||
LocationBounds bounds = null;
|
||||
if (island != null) {
|
||||
Location islandLocation = island.getLocation(worldManager.getIslandWorld(world), IslandEnvironment.Island);
|
||||
@ -138,7 +161,98 @@ public final class BlockScanner extends BukkitRunnable {
|
||||
|
||||
bounds = new LocationBounds(minX, minZ, maxX, maxZ);
|
||||
}
|
||||
|
||||
for (CachedChunk shot : subList) {
|
||||
if (!shot.isSnapshotAvailable() && !areAsyncChunksAvailable()) {
|
||||
pendingChunks.add(shot);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
processCachedChunk(world, scanY, shot, bounds);
|
||||
}
|
||||
|
||||
// Don't wait for the condition if the async chunks are available, since it would never be signalled
|
||||
if (areAsyncChunksAvailable()) {
|
||||
increment();
|
||||
|
||||
lock.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
emptyCondition.await();
|
||||
} catch (InterruptedException e) {
|
||||
// Pass the interruption
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
// process the pending chunks
|
||||
for (CachedChunk shot : readyChunks) {
|
||||
processCachedChunk(world, scanY, shot, bounds);
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
increment();
|
||||
});
|
||||
|
||||
if (!areAsyncChunksAvailable()) {
|
||||
startChunkSnapshotTask(pendingChunks, readyChunks, emptyCondition, lock);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areAsyncChunksAvailable() {
|
||||
return PaperLib.isVersion(9) && PaperLib.isPaper();
|
||||
}
|
||||
|
||||
private void startChunkSnapshotTask(List<CachedChunk> pendingChunks, List<CachedChunk> readyChunks, Condition emptyCondition, Lock lock) {
|
||||
new BukkitRunnable() {
|
||||
// The number of iterations with the pendingChunks list empty
|
||||
private int emptyIterations = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
lock.lock();
|
||||
int updatedChunks = 0;
|
||||
|
||||
Iterator<CachedChunk> chunkIterator = pendingChunks.iterator();
|
||||
|
||||
try {
|
||||
while (chunkIterator.hasNext()) {
|
||||
CachedChunk pendingChunk = chunkIterator.next();
|
||||
|
||||
if (updatedChunks >= MAX_CHUNKS_PER_ITERATION) {
|
||||
break;
|
||||
}
|
||||
|
||||
// take the snapshot
|
||||
pendingChunk.takeSnapshot();
|
||||
|
||||
chunkIterator.remove();
|
||||
readyChunks.add(pendingChunk);
|
||||
|
||||
updatedChunks++;
|
||||
}
|
||||
|
||||
if (pendingChunks.isEmpty()) {
|
||||
if (emptyIterations >= MAX_EMPTY_ITERATIONS) {
|
||||
// Send the signal to unlock the async thread and continue with the processing
|
||||
emptyCondition.signalAll();
|
||||
this.cancel();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
emptyIterations++;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(SkyBlock.getInstance(), 1, 1);
|
||||
}
|
||||
|
||||
private void processCachedChunk(World world, int scanY, CachedChunk shot, LocationBounds bounds) {
|
||||
final int cX = shot.getX() << 4;
|
||||
final int cZ = shot.getZ() << 4;
|
||||
|
||||
@ -176,9 +290,6 @@ public final class BlockScanner extends BukkitRunnable {
|
||||
}
|
||||
}
|
||||
}
|
||||
increment();
|
||||
});
|
||||
}
|
||||
|
||||
private synchronized int increment() {
|
||||
return completedNum.getAndIncrement();
|
||||
|
Loading…
Reference in New Issue
Block a user