Chunk loading rewrite

This commit is contained in:
Fabrizio La Rosa 2020-06-23 17:52:10 +02:00
parent e689218228
commit 8631b3b5cd
6 changed files with 138 additions and 151 deletions

View File

@ -116,7 +116,6 @@ public class SkyBlock extends SongodaPlugin {
Class.forName("com.destroystokyo.paper.PaperConfig");
paper = true;
paperAsync = Bukkit.spigot().getPaperConfig().getBoolean("settings.async-chunks.enable", false);
paperAsync = false;
this.getLogger().info("Enabling Paper hooks");
} catch (ClassNotFoundException ignored) {
PaperLib.suggestPaper(this);

View File

@ -1,6 +1,5 @@
package com.songoda.skyblock.biome;
import com.songoda.core.compatibility.ServerVersion;
import com.songoda.skyblock.SkyBlock;
import com.songoda.skyblock.island.Island;
import com.songoda.skyblock.island.IslandEnvironment;
@ -12,8 +11,6 @@ import org.bukkit.block.Biome;
import org.bukkit.entity.Player;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
public class BiomeManager {
@ -25,129 +22,32 @@ public class BiomeManager {
public void setBiome(Island island, Biome biome) {
Location location = island.getLocation(IslandWorld.Normal, IslandEnvironment.Island);
int radius = (int) Math.ceil(island.getRadius());
final List<ChunkSnapshot> snapshotList = new ArrayList<>(3);
if (location == null) return;
final World world = location.getWorld();
new ChunkLoader(island, IslandWorld.Normal, skyblock.isPaperAsync(), (asyncPositions, syncPositions) -> {
if (skyblock.isPaperAsync()) {
Bukkit.getScheduler().runTaskAsynchronously(skyblock, () -> {
List<Chunk> positions = new LinkedList<>();
for (CompletableFuture<Chunk> chunk : asyncPositions) {
positions.add(chunk.join());
}
for(Chunk chunk : positions){
setChunkBiome(biome, chunk);
updateBiomePacket(island, chunk);
}
//positions.stream().map(Chunk::getChunkSnapshot).forEach(snapshotList::add);
});
//Map<World, List<ChunkSnapshot>> snapshots = new HashMap<>(3);
//snapshots.put(world, snapshotList);
Bukkit.getScheduler().runTaskAsynchronously(skyblock, () -> {
//ChunkBiomeSplitter.startUpdating(snapshots, biome, (chunk) -> {
// updateBiomePacket(island, chunk);
//});
});
} else {
//syncPositions.stream().map(Chunk::getChunkSnapshot).forEach(snapshotList::add);
//Map<World, List<ChunkSnapshot>> snapshots = new HashMap<>(3);
//snapshots.put(world, snapshotList);
Bukkit.getScheduler().runTaskAsynchronously(skyblock, () -> {
//ChunkBiomeSplitter.startUpdating(snapshots, biome, (chunk) -> {
// updateBiomePacket(island, chunk);
//});
for(Chunk chunk : syncPositions){
setChunkBiome(biome, chunk);
updateBiomePacket(island, chunk);
}
});
}
if(skyblock.isPaperAsync()){
// We keep it sequentially in order to use less RAM
ChunkLoader.startChunkLoadingPerChunk(island, IslandWorld.Normal, skyblock.isPaperAsync(), (asyncChunk, syncChunk) -> {
Chunk chunk = asyncChunk.join();
setChunkBiome(biome, chunk);
updateBiomePacket(island, chunk);
});
/*Bukkit.getScheduler().runTaskAsynchronously(skyblock, () -> {
for (int x = location.getBlockX() - radius; x < location.getBlockX() + radius; x++) {
for (int z = location.getBlockZ() - radius; z < location.getBlockZ() + radius; z++) {
location.getWorld().setBiome(x, z, biome);
}
}
Bukkit.getScheduler().runTask(skyblock, () -> {
for (int x = location.getBlockX() - radius; x < location.getBlockX() + radius; x += 16) {
for (int z = location.getBlockZ() - radius; z < location.getBlockZ() + radius; z += 16) {
Chunk chunk = location.getWorld().getChunkAt(x >> 4, z >> 4);
updateBiome(island, chunk);
}
}
});
});*/
/*Bukkit.getScheduler().runTask(skyblock, () -> {
List<Chunk> chunks = new ArrayList<>();
if(skyblock.isPaperAsync()){
} else {
ChunkLoader.startChunkLoading(island, IslandWorld.Normal, skyblock.isPaperAsync(), (asyncChunks, syncChunks) -> {
Bukkit.getScheduler().runTaskAsynchronously(skyblock, () -> {
for (int x = location.getBlockX() - radius; x < location.getBlockX() + radius; x += 16) {
for (int z = location.getBlockZ() - radius; z < location.getBlockZ() + radius; z += 16) {
try {
Chunk chunk = PaperLib.getChunkAtAsync(location.getWorld(), x >> 4, z >> 4).get();
setChunkBiome(biome, chunk);
chunks.add(chunk);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
Bukkit.getScheduler().runTask(skyblock, () -> {
for(Chunk chunk : chunks){
updateBiomePacket(island, chunk);
}
});
});
} else {
int x = location.getBlockX() - radius;
Bukkit.getScheduler().runTaskTimer(skyblock, () -> {
}, 2L, 2L);
while (x < location.getBlockX() + radius) {
int z = location.getBlockZ() - radius;
while (z < location.getBlockZ() + radius) {
chunks.add(location.getWorld().getChunkAt(x >> 4, z >> 4));
z += 16;
}
x += 16;
}
Bukkit.getScheduler().runTaskAsynchronously(skyblock, () -> {
for(Chunk chunk : chunks){
syncChunks.forEach(chunk -> {
setChunkBiome(biome, chunk);
}
Bukkit.getScheduler().runTask(skyblock, () -> {
for(Chunk chunk : chunks){
updateBiomePacket(island, chunk);
}
updateBiomePacket(island, chunk);
});
});
}
});*/
});
}
}
private void setChunkBiome(Biome biome, Chunk chunk) {
//location.getWorld().loadChunk(chunk);
for(int xx = 0; xx < 16; xx++){
for(int zz = 0; zz < 16; zz++){
if(ServerVersion.isServerVersionAtLeast(ServerVersion.V1_15)){
for(int y = 0; y<256; y++){
chunk.getBlock(xx, y, zz).setBiome(biome);
}
} else {
chunk.getBlock(xx, 0, zz).setBiome(biome);
}
chunk.getBlock(xx, 0, zz).setBiome(biome);
}
}
}

View File

@ -21,7 +21,8 @@ public class ChunkLoader extends BukkitRunnable {
private ChunkScannerTask generalTask;
private ChunkForChunkScannerTask chunkTask;
private final boolean paper;
private boolean chunkForChunk;
private boolean paper;
private World world;
private int x;
private int z;
@ -29,7 +30,9 @@ public class ChunkLoader extends BukkitRunnable {
private int maxX;
private int maxZ;
private ChunkLoader(Island island, IslandWorld islandWorld, boolean paper, boolean chunkForChunk) {
private ChunkLoader(Island island, IslandWorld islandWorld, boolean paper, boolean chunkForChunk, ChunkForChunkScannerTask chunkTask) {
this.chunkTask = chunkTask;
this.chunkForChunk = chunkForChunk;
this.paper = paper;
Location islandLocation = island.getLocation(islandWorld, IslandEnvironment.Island);
@ -49,19 +52,63 @@ public class ChunkLoader extends BukkitRunnable {
x = minX;
z = minZ;
// TODO Paper
this.runTaskTimer(SkyBlock.getInstance(), 1L, 0L);
if(paper){
this.runTaskAsynchronously(SkyBlock.getInstance());
} else {
this.runTaskTimer(SkyBlock.getInstance(), 1L, 0L);
}
}
private ChunkLoader(Island island, IslandWorld islandWorld, boolean paper, boolean chunkForChunk, ChunkScannerTask generalTask) {
this.generalTask = generalTask;
this.chunkForChunk = chunkForChunk;
this.paper = paper;
Location islandLocation = island.getLocation(islandWorld, IslandEnvironment.Island);
if (islandLocation == null) return;
world = islandLocation.getWorld();
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());
minZ = Math.min(maxLocation.getBlockZ(), minLocation.getBlockZ());
maxX = Math.max(maxLocation.getBlockX(), minLocation.getBlockX());
maxZ = Math.max(maxLocation.getBlockZ(), minLocation.getBlockZ());
x = minX;
z = minZ;
if(paper){
this.runTaskAsynchronously(SkyBlock.getInstance());
} else {
this.runTaskTimer(SkyBlock.getInstance(), 1L, 0L);
}
}
@Override
public void run() {
for(int i = 0; i < 50; i++){ // TODO Config for chunk per tick
for(int i = 0; i < 50 || paper; i++){ // TODO Config for chunk per tick
if(x < maxX){
if(z < maxZ){
if(paper){
asyncPositions.add(PaperLib.getChunkAtAsync(world, x >> 4, z >> 4));
if(!chunkForChunk){
if(paper){
asyncPositions.add(PaperLib.getChunkAtAsync(world, x >> 4, z >> 4));
} else {
syncPositions.add(world.getChunkAt(x >> 4, z >> 4));
}
} else {
syncPositions.add(world.getChunkAt(x >> 4, z >> 4));
if(paper){
if(chunkTask != null) {
chunkTask.onChunkComplete(PaperLib.getChunkAtAsync(world, x >> 4, z >> 4), null);
}
} else {
if(chunkTask != null) {
chunkTask.onChunkComplete(null, world.getChunkAt(x >> 4, z >> 4));
}
}
}
z += 16;
@ -74,17 +121,17 @@ public class ChunkLoader extends BukkitRunnable {
generalTask.onComplete(asyncPositions, syncPositions);
}
this.cancel();
return;
}
}
Bukkit.broadcastMessage("Async: " + asyncPositions.size() + " Sync: " + syncPositions.size());
}
public static void startChunkLoading(Island island, IslandWorld islandWorld, boolean paper, ChunkScannerTask task){
new ChunkLoader(island, islandWorld, paper, false);
new ChunkLoader(island, islandWorld, paper, false, task);
}
public static void startChunkLoadingPerChunk(Island island, IslandWorld islandWorld, boolean paper, ChunkForChunkScannerTask task){
new ChunkLoader(island, islandWorld, paper, true);
new ChunkLoader(island, islandWorld, paper, true, task);
}
public interface ChunkScannerTask {
@ -95,7 +142,7 @@ public class ChunkLoader extends BukkitRunnable {
public interface ChunkForChunkScannerTask {
void onChunkComplete(CompletableFuture<Chunk> asyncChunks, List<Chunk> syncChunks);
void onChunkComplete(CompletableFuture<Chunk> asyncChunk, Chunk syncChunk);
}
}

View File

@ -654,7 +654,27 @@ public class IslandManager {
final World world = worldManager.getWorld(worldList);
new ChunkLoader(island, IslandWorld.Normal, skyblock.isPaperAsync(), (asyncPositions, syncPositions) -> {
if(skyblock.isPaperAsync()){
ChunkLoader.startChunkLoading(island, IslandWorld.Normal, skyblock.isPaperAsync(), (asyncChunks, syncChunks) -> {
List<Chunk> positions = new LinkedList<>();
for (CompletableFuture<Chunk> chunk : asyncChunks) {
positions.add(chunk.join());
}
snapshots.put(world, positions.stream().map(Chunk::getChunkSnapshot).collect(Collectors.toList()));
ChunkDeleteSplitter.startDeletion(snapshots);
});
} else {
ChunkLoader.startChunkLoading(island, IslandWorld.Normal, skyblock.isPaperAsync(), (asyncChunks, syncChunks) -> {
Bukkit.getScheduler().runTask(skyblock, () -> {
final List<ChunkSnapshot> list = syncChunks.stream().map(Chunk::getChunkSnapshot).collect(Collectors.toList());
snapshots.put(world, list);
ChunkDeleteSplitter.startDeletion(snapshots);
});
});
}
/*new ChunkLoader(island, IslandWorld.Normal, skyblock.isPaperAsync(), (asyncPositions, syncPositions) -> {
if(skyblock.isPaperAsync()){
Bukkit.getScheduler().runTaskAsynchronously(skyblock, () -> {
List<Chunk> positions = new LinkedList<>();
@ -670,7 +690,7 @@ public class IslandManager {
snapshots.put(world, list);
ChunkDeleteSplitter.startDeletion(snapshots);
}
});
});*/
}
}

View File

@ -27,7 +27,7 @@ public class ChunkDeleteSplitter extends BukkitRunnable {
}
private void start() {
BlockScanner.startScanner(snapshots, false, false, (blocks) -> {
BlockScanner.startScanner(snapshots, true, true, false, (blocks) -> {
this.blocks = blocks;
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});

View File

@ -65,32 +65,48 @@ public final class IslandScan extends BukkitRunnable {
if (skyblock.isPaperAsync()) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
populate(snapshots, IslandWorld.Normal, true);
if (hasNether) populate(snapshots, IslandWorld.Nether, true);
if (hasEnd) populate(snapshots, IslandWorld.End, true);
initScan(skyblock, hasNether, hasEnd, snapshots);
});
} else {
initScan(skyblock, hasNether, hasEnd, snapshots);
}
BlockScanner.startScanner(snapshots, false, false, (blocks) -> {
return this;
}
private void initScan(SkyBlock skyblock, boolean hasNether, boolean hasEnd, Map<World, List<ChunkSnapshot>> snapshots) {
populate(snapshots, IslandWorld.Normal, skyblock.isPaperAsync(), () -> {
if (hasNether) {
populate(snapshots, IslandWorld.Nether, skyblock.isPaperAsync(), () -> {
if (hasEnd) {
populate(snapshots, IslandWorld.End, skyblock.isPaperAsync(), () -> {
BlockScanner.startScanner(snapshots, true, true, false, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});
});
} else {
BlockScanner.startScanner(snapshots, true, true, false, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});
}
});
} else {
BlockScanner.startScanner(snapshots, true, true, false, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});
});
} else {
populate(snapshots, IslandWorld.Normal, false);
if (hasNether) populate(snapshots, IslandWorld.Nether, false);
if (hasEnd) populate(snapshots, IslandWorld.End, false);
BlockScanner.startScanner(snapshots, false, false, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});
}
return this;
}
});
}
private void finalizeBlocks() {
@ -178,11 +194,11 @@ public final class IslandScan extends BukkitRunnable {
});
}
private void populate(Map<World, List<ChunkSnapshot>> snapshots, IslandWorld world, boolean paper) {
private void populate(Map<World, List<ChunkSnapshot>> snapshots, IslandWorld world, boolean paper, PopulateTask task) {
final SkyBlock skyblock = SkyBlock.getInstance();
new ChunkLoader(island, IslandWorld.Normal, paper, (asyncPositions, syncPositions) -> {
ChunkLoader.startChunkLoading(island, IslandWorld.Normal, paper, (asyncPositions, syncPositions) -> {
if(paper){
List<ChunkSnapshot> positions = new LinkedList<>();
for(CompletableFuture<Chunk> chunk : asyncPositions){
@ -192,9 +208,14 @@ public final class IslandScan extends BukkitRunnable {
} else {
snapshots.put(skyblock.getWorldManager().getWorld(world), syncPositions.stream().map(org.bukkit.Chunk::getChunkSnapshot).collect(Collectors.toList()));
}
task.onComplete();
});
}
private interface PopulateTask {
void onComplete();
}
public Set<Location> getDoubleBlocks() {
return doubleBlocks;
}