Initial work on Chunk loading update

This commit is contained in:
Fabrizio La Rosa 2020-06-23 05:43:35 +02:00
parent 43925eddcf
commit e689218228
9 changed files with 338 additions and 123 deletions

View File

@ -116,6 +116,7 @@ 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

@ -5,15 +5,15 @@ import com.songoda.skyblock.SkyBlock;
import com.songoda.skyblock.island.Island;
import com.songoda.skyblock.island.IslandEnvironment;
import com.songoda.skyblock.island.IslandWorld;
import com.songoda.skyblock.blockscanner.ChunkLoader;
import com.songoda.skyblock.utils.version.NMSUtil;
import io.papermc.lib.PaperLib;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.*;
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 {
@ -27,6 +27,52 @@ public class BiomeManager {
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);
}
});
}
});
/*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++) {
@ -42,33 +88,56 @@ public class BiomeManager {
}
});
});*/
/*Bukkit.getScheduler().runTask(skyblock, () -> {
List<Chunk> chunks = new ArrayList<>();
Bukkit.getScheduler().runTaskAsynchronously(skyblock, () -> {
long i = 0;
for (int x = location.getBlockX() - radius; x < location.getBlockX() + radius; x += 16) {
for (int z = location.getBlockZ() - radius; z < location.getBlockZ() + radius; z += 16) {
int finalX = x;
int finalZ = z;
if(skyblock.isPaperAsync()){
PaperLib.getChunkAtAsync(location.getWorld(), finalX >> 4, finalZ >> 4).thenAccept(chunk -> {
setChunkBiome(island, biome, chunk);
});
} else {
Bukkit.getScheduler().runTaskLater(skyblock, () -> {
Chunk chunk = location.getWorld().getChunkAt(finalX >> 4, finalZ >> 4);
setChunkBiome(island, biome, chunk);
}, i);
i++;
if(skyblock.isPaperAsync()){
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){
setChunkBiome(biome, chunk);
}
Bukkit.getScheduler().runTask(skyblock, () -> {
for(Chunk chunk : chunks){
updateBiomePacket(island, chunk);
}
});
});
}
});*/
}
private void setChunkBiome(Island island, Biome biome, Chunk 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++){
@ -77,12 +146,10 @@ public class BiomeManager {
chunk.getBlock(xx, y, zz).setBiome(biome);
}
} else {
chunk.getBlock(xx, 128, zz).setBiome(biome);
chunk.getBlock(xx, 0, zz).setBiome(biome);
}
}
}
updateBiomePacket(island, chunk);
}
private Class<?> packetPlayOutMapChunkClass;

View File

@ -0,0 +1,85 @@
package com.songoda.skyblock.biome;
import com.songoda.skyblock.SkyBlock;
import com.songoda.skyblock.blockscanner.BlockInfo;
import com.songoda.skyblock.blockscanner.BlockScanner;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
public class ChunkBiomeSplitter extends BukkitRunnable {
private final Map<World, List<ChunkSnapshot>> snapshots;
private Queue<BlockInfo> blocks;
private final Biome biome;
private Chunk lastChunk;
private ChunkBiomeTask task;
private ChunkBiomeSplitter(Map<World, List<ChunkSnapshot>> snapshots, Biome biome, ChunkBiomeTask task) {
this.task = task;
this.snapshots = snapshots;
this.biome = biome;
lastChunk = null;
start();
}
private void start() {
Bukkit.getScheduler().runTaskAsynchronously(SkyBlock.getInstance(), () -> {
BlockScanner.startScanner(snapshots, true, true, true, (blocks) -> {
this.blocks = blocks;
this.runTaskTimer(SkyBlock.getInstance(), 2L, 2L);
});
});
}
@Override
public void run() {
int updateAmount = 0;
for (Iterator<BlockInfo> it = blocks.iterator(); it.hasNext();) {
if (updateAmount == 3500) break;
final BlockInfo pair = it.next();
final Block block = pair.getWorld().getBlockAt(pair.getX(), pair.getY(), pair.getZ());
if(!block.getChunk().equals(lastChunk)){
lastChunk = block.getChunk();
task.onChunkComplete(lastChunk);
}
block.setBiome(biome);
updateAmount++;
it.remove();
}
Bukkit.broadcastMessage("Amount: " + blocks.size() + " Empty: " + blocks.isEmpty());
if (blocks.isEmpty()) {
super.cancel();
}
}
public static void startUpdating(Map<World, List<ChunkSnapshot>> snapshots, Biome biome, ChunkBiomeTask task) {
new ChunkBiomeSplitter(snapshots, biome, task);
}
public interface ChunkBiomeTask {
void onChunkComplete(Chunk chunk);
}
}

View File

@ -59,9 +59,12 @@ public final class BlockScanner extends BukkitRunnable {
private final Queue<BlockInfo> blocks;
private final ScannerTasks tasks;
private int scanY;
private boolean ignoreLiquids;
private boolean ignoreAir;
private BlockScanner(Map<World, List<ChunkSnapshot>> snapshots, ScannerTasks tasks) {
private BlockScanner(Map<World, List<ChunkSnapshot>> snapshots, boolean ignoreLiquids, boolean ignoreAir, boolean ignoreY, ScannerTasks tasks) {
this.ignoreLiquids = ignoreLiquids;
this.ignoreAir = ignoreAir;
this.blocks = new ConcurrentLinkedQueue<>();
this.tasks = tasks;
@ -92,8 +95,15 @@ public final class BlockScanner extends BukkitRunnable {
final ConfigurationSection liquidSection = config.getConfigurationSection("Island.World." + env + ".Liquid");
int startY;
if(ignoreY){
startY = 255;
} else {
startY = !ignoreLiquids && liquidSection.getBoolean("Enable") && !config.getBoolean("Island.Levelling.ScanLiquid") ? liquidSection.getInt("Height") + 1 : 0;
}
for (List<ChunkSnapshot> sub : parts) {
queueWork(world, liquidSection.getBoolean("Enable") && !config.getBoolean("Island.Levelling.ScanLiquid") ? liquidSection.getInt("Height") + 1 : 0, sub);
queueWork(world, startY, sub);
}
}
@ -102,8 +112,6 @@ public final class BlockScanner extends BukkitRunnable {
}
private void queueWork(World world, int scanY, List<ChunkSnapshot> subList) {
Bukkit.getServer().getScheduler().runTaskAsynchronously(SkyBlock.getInstance(), () -> {
for (ChunkSnapshot shot : subList) {
@ -119,7 +127,13 @@ public final class BlockScanner extends BukkitRunnable {
? shot.getBlockType(x, y, z) : MaterialIDHelper.getLegacyMaterial(getBlockTypeID(shot, x, y, z)));
if (type == null || type == CompatibleMaterial.AIR || type == CompatibleMaterial.WATER) continue;
if(type == null){
continue;
} else if(type.equals(CompatibleMaterial.AIR) && ignoreAir){
continue;
} else if(type.equals(CompatibleMaterial.WATER) && ignoreLiquids){
continue;
}
blocks.add(new BlockInfo(world, x + (cX), y, z + (cZ)));
}
@ -147,17 +161,17 @@ public final class BlockScanner extends BukkitRunnable {
cancel();
}
public static void startScanner(Map<World, List<ChunkSnapshot>> snapshots, ScannerTasks tasks) {
public static void startScanner(Map<World, List<ChunkSnapshot>> snapshots, boolean ignoreLiquids, 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");
final BlockScanner scanner = new BlockScanner(snapshots, tasks);
final BlockScanner scanner = new BlockScanner(snapshots, ignoreLiquids, ignoreAir, ignoreY, tasks);
scanner.runTaskTimer(SkyBlock.getInstance(), 5, 5);
}
public static interface ScannerTasks {
public interface ScannerTasks {
void onComplete(Queue<BlockInfo> blocks);

View File

@ -0,0 +1,101 @@
package com.songoda.skyblock.blockscanner;
import com.sk89q.worldedit.bukkit.paperlib.PaperLib;
import com.songoda.skyblock.SkyBlock;
import com.songoda.skyblock.island.Island;
import com.songoda.skyblock.island.IslandEnvironment;
import com.songoda.skyblock.island.IslandWorld;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class ChunkLoader extends BukkitRunnable {
public final List<CompletableFuture<Chunk>> asyncPositions = new LinkedList<>();
public final List<Chunk> syncPositions = new LinkedList<>();
private ChunkScannerTask generalTask;
private ChunkForChunkScannerTask chunkTask;
private final boolean paper;
private World world;
private int x;
private int z;
private int minZ;
private int maxX;
private int maxZ;
private ChunkLoader(Island island, IslandWorld islandWorld, boolean paper, boolean 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;
// TODO Paper
this.runTaskTimer(SkyBlock.getInstance(), 1L, 0L);
}
@Override
public void run() {
for(int i = 0; i < 50; i++){ // TODO Config for chunk per tick
if(x < maxX){
if(z < maxZ){
if(paper){
asyncPositions.add(PaperLib.getChunkAtAsync(world, x >> 4, z >> 4));
} else {
syncPositions.add(world.getChunkAt(x >> 4, z >> 4));
}
z += 16;
} else {
z = minZ;
x += 16;
}
} else {
if(generalTask != null) {
generalTask.onComplete(asyncPositions, syncPositions);
}
this.cancel();
}
}
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);
}
public static void startChunkLoadingPerChunk(Island island, IslandWorld islandWorld, boolean paper, ChunkForChunkScannerTask task){
new ChunkLoader(island, islandWorld, paper, true);
}
public interface ChunkScannerTask {
void onComplete(List<CompletableFuture<Chunk>> asyncChunks, List<Chunk> syncChunks);
}
public interface ChunkForChunkScannerTask {
void onChunkComplete(CompletableFuture<Chunk> asyncChunks, List<Chunk> syncChunks);
}
}

View File

@ -1,7 +1,6 @@
package com.songoda.skyblock.island;
import com.bekvon.bukkit.residence.Residence;
import com.bekvon.bukkit.residence.api.ResidenceApi;
import com.bekvon.bukkit.residence.containers.Flags;
import com.bekvon.bukkit.residence.protection.ClaimedResidence;
import com.google.common.base.Preconditions;
@ -18,7 +17,7 @@ import com.songoda.skyblock.cooldown.CooldownType;
import com.songoda.skyblock.invite.Invite;
import com.songoda.skyblock.invite.InviteManager;
import com.songoda.skyblock.island.removal.ChunkDeleteSplitter;
import com.songoda.skyblock.levelling.ChunkUtil;
import com.songoda.skyblock.blockscanner.ChunkLoader;
import com.songoda.skyblock.message.MessageManager;
import com.songoda.skyblock.playerdata.PlayerData;
import com.songoda.skyblock.playerdata.PlayerDataManager;
@ -53,7 +52,6 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.IOException;
@ -559,7 +557,7 @@ public class IslandManager {
}
}
startDeletition(island, worldManager);
startDeletion(island, worldManager);
skyblock.getVisitManager().deleteIsland(island.getOwnerUUID());
skyblock.getBanManager().deleteIsland(island.getOwnerUUID());
@ -645,7 +643,7 @@ public class IslandManager {
return true;
}
private void startDeletition(Island island, WorldManager worldManager) {
private void startDeletion(Island island, WorldManager worldManager) {
final Map<World, List<ChunkSnapshot>> snapshots = new HashMap<>(3);
for (IslandWorld worldList : IslandWorld.getIslandWorlds()) {
@ -656,27 +654,25 @@ public class IslandManager {
final World world = worldManager.getWorld(worldList);
ChunkUtil chunks = new ChunkUtil();
if (skyblock.isPaperAsync()) {
chunks.getChunksToScan(island, worldList, true);
Bukkit.getScheduler().runTaskAsynchronously(skyblock, () -> {
List<Chunk> positions = new LinkedList<>();
for (CompletableFuture<Chunk> chunk : chunks.asyncPositions) {
positions.add(chunk.join());
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());
}
snapshots.put(world, positions.stream().map(Chunk::getChunkSnapshot).collect(Collectors.toList()));
}
});
} else {
chunks.getChunksToScan(island, worldList, false);
final List<ChunkSnapshot> list = chunks.syncPositions.stream().map(Chunk::getChunkSnapshot).collect(Collectors.toList());
ChunkDeleteSplitter.startDeletion(snapshots);
});
} else {
final List<ChunkSnapshot> list = syncPositions.stream().map(Chunk::getChunkSnapshot).collect(Collectors.toList());
snapshots.put(world, list);
}
snapshots.put(world, list);
ChunkDeleteSplitter.startDeletion(snapshots);
}
});
}
ChunkDeleteSplitter.startDeletion(snapshots);
}
public synchronized void deleteIslandData(UUID uuid) {

View File

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

View File

@ -1,48 +0,0 @@
package com.songoda.skyblock.levelling;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import com.sk89q.worldedit.bukkit.paperlib.PaperLib;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import com.songoda.skyblock.island.Island;
import com.songoda.skyblock.island.IslandEnvironment;
import com.songoda.skyblock.island.IslandWorld;
public class ChunkUtil {
public final List<CompletableFuture<Chunk>> asyncPositions = new LinkedList<>();
public final List<Chunk> syncPositions = new LinkedList<>();
public void getChunksToScan(Island island, IslandWorld islandWorld, boolean paper) {
Location islandLocation = island.getLocation(islandWorld, IslandEnvironment.Island);
if (islandLocation == null) return;
World 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());
int minZ = Math.min(maxLocation.getBlockZ(), minLocation.getBlockZ());
int maxX = Math.max(maxLocation.getBlockX(), minLocation.getBlockX());
int maxZ = Math.max(maxLocation.getBlockZ(), minLocation.getBlockZ());
for (int x = minX; x < maxX + 16; x += 16) {
for (int z = minZ; z < maxZ + 16; z += 16) {
if(paper){
asyncPositions.add(PaperLib.getChunkAtAsync(world, x >> 4, z >> 4));
} else {
syncPositions.add(world.getChunkAt(x >> 4, z >> 4));
}
}
}
}
}

View File

@ -8,7 +8,7 @@ import com.songoda.skyblock.blockscanner.BlockScanner;
import com.songoda.skyblock.island.Island;
import com.songoda.skyblock.island.IslandLevel;
import com.songoda.skyblock.island.IslandWorld;
import com.songoda.skyblock.levelling.ChunkUtil;
import com.songoda.skyblock.blockscanner.ChunkLoader;
import com.songoda.skyblock.levelling.rework.amount.AmountMaterialPair;
import com.songoda.skyblock.levelling.rework.amount.BlockAmount;
import com.songoda.skyblock.message.MessageManager;
@ -69,7 +69,7 @@ public final class IslandScan extends BukkitRunnable {
if (hasNether) populate(snapshots, IslandWorld.Nether, true);
if (hasEnd) populate(snapshots, IslandWorld.End, true);
BlockScanner.startScanner(snapshots, (blocks) -> {
BlockScanner.startScanner(snapshots, false, false, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
@ -81,7 +81,7 @@ public final class IslandScan extends BukkitRunnable {
if (hasNether) populate(snapshots, IslandWorld.Nether, false);
if (hasEnd) populate(snapshots, IslandWorld.End, false);
BlockScanner.startScanner(snapshots, (blocks) -> {
BlockScanner.startScanner(snapshots, false, false, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
@ -182,18 +182,17 @@ public final class IslandScan extends BukkitRunnable {
final SkyBlock skyblock = SkyBlock.getInstance();
ChunkUtil chunks = new ChunkUtil();
chunks.getChunksToScan(island, world, paper);
if(paper){
List<ChunkSnapshot> positions = new LinkedList<>();
for(CompletableFuture<Chunk> chunk : chunks.asyncPositions){
positions.add(chunk.join().getChunkSnapshot());
new ChunkLoader(island, IslandWorld.Normal, paper, (asyncPositions, syncPositions) -> {
if(paper){
List<ChunkSnapshot> positions = new LinkedList<>();
for(CompletableFuture<Chunk> chunk : asyncPositions){
positions.add(chunk.join().getChunkSnapshot());
}
snapshots.put(skyblock.getWorldManager().getWorld(world), positions);
} else {
snapshots.put(skyblock.getWorldManager().getWorld(world), syncPositions.stream().map(org.bukkit.Chunk::getChunkSnapshot).collect(Collectors.toList()));
}
snapshots.put(skyblock.getWorldManager().getWorld(world), positions);
} else {
snapshots.put(skyblock.getWorldManager().getWorld(world), chunks.syncPositions.stream().map(org.bukkit.Chunk::getChunkSnapshot).collect(Collectors.toList()));
}
});
}
public Set<Location> getDoubleBlocks() {