Improved chunk loading performance.

This commit is contained in:
Brianna 2020-09-30 18:30:25 -05:00
parent f4e13482e5
commit ffa5ad1bee
7 changed files with 125 additions and 56 deletions

View File

@ -56,8 +56,8 @@ public class BiomeManager {
int chunkAmount = (int) Math.ceil(Math.pow(island.getSize()/16d, 2d));
AtomicInteger progress = new AtomicInteger();
ChunkLoader.startChunkLoadingPerChunk(island, world, plugin.isPaperAsync(), (futureChunk) -> {
Chunk chunk = futureChunk.join();
ChunkLoader.startChunkLoadingPerChunk(island, world, plugin.isPaperAsync(), (cachedChunk) -> {
Chunk chunk = cachedChunk.getChunk();
try {
if (chunk != null)
biome.setBiome(chunk);

View File

@ -39,12 +39,12 @@ public final class BlockScanner extends BukkitRunnable {
ID_FIELD = temp;
}
public static int getBlockTypeID(ChunkSnapshot snapshot, int x, int y, int z) {
public static int getBlockTypeID(CachedChunk chunk, int x, int y, int z) {
int id = 0;
try {
id = (Integer) ID_FIELD.invoke(snapshot, x, y, z);
id = (Integer) ID_FIELD.invoke(chunk.getSnapshot(), x, y, z);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
@ -63,7 +63,7 @@ public final class BlockScanner extends BukkitRunnable {
private final boolean ignoreLiquids;
private final boolean ignoreAir;
private BlockScanner(Map<World, List<ChunkSnapshot>> snapshots,
private BlockScanner(Map<World, List<CachedChunk>> snapshots,
Island island,
boolean ignoreLiquids,
boolean ignoreLiquidsY,
@ -81,9 +81,9 @@ public final class BlockScanner extends BukkitRunnable {
int threadCount = 0;
for (Entry<World, List<ChunkSnapshot>> entry : snapshots.entrySet()) {
for (Entry<World, List<CachedChunk>> entry : snapshots.entrySet()) {
final List<List<ChunkSnapshot>> parts = Lists.partition(entry.getValue(), 16);
final List<List<CachedChunk>> parts = Lists.partition(entry.getValue(), 16);
threadCount += parts.size();
@ -111,7 +111,7 @@ public final class BlockScanner extends BukkitRunnable {
startY = !ignoreLiquidsY && liquidSection.getBoolean("Enable") && !config.getBoolean("Island.Levelling.ScanLiquid") ? liquidSection.getInt("Height") + 1 : 0;
}
for (List<ChunkSnapshot> sub : parts) {
for (List<CachedChunk> sub : parts) {
queueWork(world, startY, sub);
}
}
@ -119,7 +119,7 @@ public final class BlockScanner extends BukkitRunnable {
this.threadCount = threadCount;
}
private void queueWork(World world, int scanY, List<ChunkSnapshot> subList) {
private void queueWork(World world, int scanY, List<CachedChunk> subList) {
WorldManager worldManager = SkyBlock.getInstance().getWorldManager();
Bukkit.getServer().getScheduler().runTaskAsynchronously(SkyBlock.getInstance(), () -> {
@ -138,7 +138,7 @@ public final class BlockScanner extends BukkitRunnable {
bounds = new LocationBounds(minX, minZ, maxX, maxZ);
}
for (ChunkSnapshot shot : subList) {
for (CachedChunk shot : subList) {
final int cX = shot.getX() << 4;
final int cZ = shot.getZ() << 4;
@ -160,7 +160,7 @@ public final class BlockScanner extends BukkitRunnable {
for (int y = scanY; y < world.getMaxHeight(); y++) {
final CompatibleMaterial type = CompatibleMaterial.getBlockMaterial(
ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)
? shot.getBlockType(x, y, z) :
? shot.getSnapshot().getBlockType(x, y, z) :
MaterialIDHelper.getLegacyMaterial(getBlockTypeID(shot, x, y, z)));
if(type == null){
@ -196,7 +196,7 @@ public final class BlockScanner extends BukkitRunnable {
cancel();
}
public static void startScanner(Map<World, List<ChunkSnapshot>> snapshots, Island island, boolean ignoreLiquids, boolean ignoreLiquidsY, boolean ignoreAir, boolean ignoreY, ScannerTasks tasks) {
public static void startScanner(Map<World, List<CachedChunk>> snapshots, Island island, boolean ignoreLiquids, boolean ignoreLiquidsY, boolean ignoreAir, boolean ignoreY, ScannerTasks tasks) {
if (snapshots == null) throw new IllegalArgumentException("snapshots cannot be null");
if (tasks == null) throw new IllegalArgumentException("tasks cannot be null");

View File

@ -0,0 +1,73 @@
package com.songoda.skyblock.blockscanner;
import io.papermc.lib.PaperLib;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
import java.util.Objects;
public class CachedChunk {
private final String world;
private final int x;
private final int z;
private ChunkSnapshot latestSnapshot;
public CachedChunk(Chunk chunk) {
this(chunk.getWorld().getName(), chunk.getX(), chunk.getZ());
}
public CachedChunk(String world, int x, int z) {
this.world = world;
this.x = x;
this.z = z;
}
public CachedChunk(World world, int x, int z) {
this(world.getName(), x, z);
}
public String getWorld() {
return this.world;
}
public int getX() {
return this.x;
}
public int getZ() {
return this.z;
}
public Chunk getChunk() {
World world = Bukkit.getWorld(this.world);
if (world == null)
return null;
return PaperLib.getChunkAtAsync(world, this.x, this.z).join();
}
public ChunkSnapshot getSnapshot() {
if (latestSnapshot == null)
return takeSnapshot();
return latestSnapshot;
}
public ChunkSnapshot takeSnapshot() {
return this.latestSnapshot = getChunk().getChunkSnapshot();
}
@Override
public boolean equals(Object o) {
if (o instanceof Chunk) {
Chunk other = (Chunk) o;
return this.world.equals(other.getWorld().getName()) && this.x == other.getX() && this.z == other.getZ();
} else return false;
}
@Override
public int hashCode() {
return Objects.hash(this.world, this.x, this.z);
}
}

View File

@ -4,19 +4,17 @@ import com.songoda.skyblock.SkyBlock;
import com.songoda.skyblock.island.Island;
import com.songoda.skyblock.island.IslandEnvironment;
import com.songoda.skyblock.island.IslandWorld;
import io.papermc.lib.PaperLib;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class ChunkLoader extends BukkitRunnable {
public final List<CompletableFuture<Chunk>> positions = new LinkedList<>();
public final List<CachedChunk> positions = new LinkedList<>();
private ChunkScannerTask generalTask;
private ChunkForChunkScannerTask chunkTask;
@ -54,7 +52,7 @@ public class ChunkLoader extends BukkitRunnable {
Location minLocation = new Location(world, islandLocation.getBlockX() - island.getRadius(), 0, islandLocation.getBlockZ() - island.getRadius());
Location maxLocation = new Location(world, islandLocation.getBlockX() + island.getRadius(), world.getMaxHeight(), islandLocation.getBlockZ() + island.getRadius());
int minX = Math.min(maxLocation.getBlockX(), minLocation.getBlockX()) >> 4 << 4 ;
int minX = Math.min(maxLocation.getBlockX(), minLocation.getBlockX()) >> 4 << 4;
minZ = Math.min(maxLocation.getBlockZ(), minLocation.getBlockZ()) >> 4 << 4;
maxX = Math.max(maxLocation.getBlockX(), minLocation.getBlockX()) >> 4 << 4 | 15;
@ -62,8 +60,8 @@ public class ChunkLoader extends BukkitRunnable {
x = minX;
z = minZ;
if(paper){
if (paper) {
this.runTaskAsynchronously(SkyBlock.getInstance());
} else {
this.runTaskTimer(SkyBlock.getInstance(), 1L, 0L);
@ -77,7 +75,7 @@ public class ChunkLoader extends BukkitRunnable {
ChunkScannerTask generalTask,
CompleteTask complete) {
chunkPerTick = SkyBlock.getInstance().getConfiguration().getInt("Island.Performance.ChunkPerTick", 25);
this.completeTask = complete;
this.generalTask = generalTask;
this.chunkForChunk = chunkForChunk;
@ -99,7 +97,7 @@ public class ChunkLoader extends BukkitRunnable {
islandLocation.getBlockX() + island.getRadius(),
world.getMaxHeight(),
islandLocation.getBlockZ() + island.getRadius());
int minX = Math.min(maxLocation.getBlockX(), minLocation.getBlockX()) >> 4 << 4;
minZ = Math.min(maxLocation.getBlockZ(), minLocation.getBlockZ()) >> 4 << 4;
@ -108,8 +106,8 @@ public class ChunkLoader extends BukkitRunnable {
x = minX;
z = minZ;
if(paper){
if (paper) {
this.runTaskAsynchronously(SkyBlock.getInstance());
} else {
this.runTaskTimer(SkyBlock.getInstance(), 1L, 0L);
@ -118,14 +116,14 @@ public class ChunkLoader extends BukkitRunnable {
@Override
public void run() { // TODO New algorithm that start from the center of the island
for(int i = 0; i < chunkPerTick || paper; i++){
if(x <= maxX){
if(z <= maxZ){
if(!chunkForChunk){
positions.add(PaperLib.getChunkAtAsync(world, x >> 4, z >> 4));
for (int i = 0; i < chunkPerTick || paper; i++) {
if (x <= maxX) {
if (z <= maxZ) {
if (!chunkForChunk) {
positions.add(new CachedChunk(world, x >> 4, z >> 4));
} else {
if(chunkTask != null) {
chunkTask.onChunkComplete(PaperLib.getChunkAtAsync(world, x >> 4, z >> 4));
if (chunkTask != null) {
chunkTask.onChunkComplete(new CachedChunk(world, x >> 4, z >> 4));
}
}
@ -135,10 +133,10 @@ public class ChunkLoader extends BukkitRunnable {
x += 16;
}
} else {
if(generalTask != null) {
if (generalTask != null) {
generalTask.onComplete(positions);
}
if(completeTask != null) {
if (completeTask != null) {
completeTask.onComplete(island);
}
this.cancel();
@ -147,22 +145,22 @@ public class ChunkLoader extends BukkitRunnable {
}
}
public static void startChunkLoading(Island island, IslandWorld islandWorld, boolean paper, ChunkScannerTask task, CompleteTask complete){
public static void startChunkLoading(Island island, IslandWorld islandWorld, boolean paper, ChunkScannerTask task, CompleteTask complete) {
new ChunkLoader(island, islandWorld, paper, false, task, complete);
}
public static void startChunkLoadingPerChunk(Island island, IslandWorld islandWorld, boolean paper, ChunkForChunkScannerTask task, CompleteTask complete){
public static void startChunkLoadingPerChunk(Island island, IslandWorld islandWorld, boolean paper, ChunkForChunkScannerTask task, CompleteTask complete) {
new ChunkLoader(island, islandWorld, paper, true, task, complete);
}
public interface ChunkScannerTask {
void onComplete(List<CompletableFuture<Chunk>> chunks);
void onComplete(List<CachedChunk> chunks);
}
public interface ChunkForChunkScannerTask {
void onChunkComplete(CompletableFuture<Chunk> chunk);
void onChunkComplete(CachedChunk chunk);
}
public interface CompleteTask {
void onComplete(Island island);
}

View File

@ -12,6 +12,7 @@ import com.songoda.core.compatibility.ServerVersion;
import com.songoda.skyblock.SkyBlock;
import com.songoda.skyblock.api.event.island.*;
import com.songoda.skyblock.ban.BanManager;
import com.songoda.skyblock.blockscanner.CachedChunk;
import com.songoda.skyblock.blockscanner.ChunkLoader;
import com.songoda.skyblock.config.FileManager;
import com.songoda.skyblock.config.FileManager.Config;
@ -625,7 +626,7 @@ public class IslandManager {
}
private void startDeletion(Island island, WorldManager worldManager) {
final Map<World, List<ChunkSnapshot>> snapshots = new HashMap<>(3);
final Map<World, List<CachedChunk>> cachedChunks = new HashMap<>(3);
for (IslandWorld worldList : IslandWorld.getIslandWorlds()) {
@ -636,12 +637,8 @@ public class IslandManager {
final World world = worldManager.getWorld(worldList);
ChunkLoader.startChunkLoading(island, IslandWorld.Normal, plugin.isPaperAsync(), (chunks) -> {
List<Chunk> positions = new LinkedList<>();
for (CompletableFuture<Chunk> chunk : chunks) {
positions.add(chunk.join());
}
snapshots.put(world, positions.stream().map(Chunk::getChunkSnapshot).collect(Collectors.toList()));
ChunkDeleteSplitter.startDeletion(snapshots);
cachedChunks.put(world, chunks);
ChunkDeleteSplitter.startDeletion(cachedChunks);
}, null);
}

View File

@ -4,6 +4,7 @@ import com.songoda.core.compatibility.CompatibleMaterial;
import com.songoda.skyblock.SkyBlock;
import com.songoda.skyblock.blockscanner.BlockInfo;
import com.songoda.skyblock.blockscanner.BlockScanner;
import com.songoda.skyblock.blockscanner.CachedChunk;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
import org.bukkit.block.Block;
@ -16,16 +17,16 @@ import java.util.Queue;
public class ChunkDeleteSplitter extends BukkitRunnable {
private final Map<World, List<ChunkSnapshot>> snapshots;
private final Map<World, List<CachedChunk>> cachedChunks;
private Queue<BlockInfo> blocks;
private ChunkDeleteSplitter(Map<World, List<ChunkSnapshot>> snapshots) {
this.snapshots = snapshots;
private ChunkDeleteSplitter(Map<World, List<CachedChunk>> cachedChunks) {
this.cachedChunks = cachedChunks;
start();
}
private void start() {
BlockScanner.startScanner(snapshots, null,false, true, true, false, (blocks) -> {
BlockScanner.startScanner(cachedChunks, null,false, true, true, false, (blocks) -> {
this.blocks = blocks;
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});
@ -54,7 +55,7 @@ public class ChunkDeleteSplitter extends BukkitRunnable {
}
}
public static void startDeletion(Map<World, List<ChunkSnapshot>> snapshots) {
public static void startDeletion(Map<World, List<CachedChunk>> snapshots) {
new ChunkDeleteSplitter(snapshots);
}

View File

@ -5,6 +5,7 @@ import com.songoda.skyblock.SkyBlock;
import com.songoda.skyblock.api.event.island.IslandLevelChangeEvent;
import com.songoda.skyblock.blockscanner.BlockInfo;
import com.songoda.skyblock.blockscanner.BlockScanner;
import com.songoda.skyblock.blockscanner.CachedChunk;
import com.songoda.skyblock.blockscanner.ChunkLoader;
import com.songoda.skyblock.island.Island;
import com.songoda.skyblock.island.IslandLevel;
@ -78,10 +79,10 @@ public final class IslandScan extends BukkitRunnable {
private void initScan(SkyBlock plugin) {
final Map<World, List<ChunkSnapshot>> snapshots = new HashMap<>(3);
final Map<World, List<CachedChunk>> cachedChunk = new HashMap<>(3);
populate(snapshots, plugin.isPaperAsync(), () -> {
BlockScanner.startScanner(snapshots, island, true, true, true, false, (blocks) -> {
populate(cachedChunk, plugin.isPaperAsync(), () -> {
BlockScanner.startScanner(cachedChunk, island, true, true, true, false, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
@ -130,15 +131,14 @@ public final class IslandScan extends BukkitRunnable {
}
}
private void populate(Map<World, List<ChunkSnapshot>> snapshots, boolean paper, PopulateTask task) {
private void populate(Map<World, List<CachedChunk>> cachedChunks, boolean paper, PopulateTask task) {
final SkyBlock plugin = SkyBlock.getInstance();
List<ChunkSnapshot> positions = new LinkedList<>();
List<CachedChunk> positions = new LinkedList<>();
ChunkLoader.startChunkLoadingPerChunk(island, world, paper, (chunkCompletableFuture) ->
positions.add(chunkCompletableFuture.join().getChunkSnapshot()),
ChunkLoader.startChunkLoadingPerChunk(island, world, paper, positions::add,
value -> {
snapshots.put(plugin.getWorldManager().getWorld(world), positions);
cachedChunks.put(plugin.getWorldManager().getWorld(world), positions);
task.onComplete();
});
}